The new siamltex layout causes (or reveals) some problems with how document classes "use" modules. Originally, they didn't. The user selects modules, not the document class. But once we modularized the AMS classes (per JMarc's suggestion), this lack became a problem, because the AMS classes would always load without any theorem environments. You could add them, but you had to do it manually. So we introduced a (poorly named) "UseModule" tag that allows a layout to declare a "default" module that is to be used with the document class, unless the user removes it or something. So, e.g., if create a new document and select "article (AMS)", then the "Theorems (AMS)" modules will get selected automatically. We thus get the theorem environments by default but preserve the extra flexibility modules bring.

In the siamltex layout, though, it turns out to be easiest by far to "Input" the code from the theorems-ams module rather than to UseModule it. This is because the layout wants to NoStyle several of those environments (which SIAM doesn't accept), and it can't do so if they're in a module: UseModule does not load the module, only mark it as a default. But once the module is Input and then NoStyle'd, there is a possibility that a user might try to use that very module, or worse use an incompatible one, which will lead to uncompilable documents. Or the user might want to use modules that need theorems-ams to be loaded, which she ought to be able to do, since siamltex provides it, but she can't because LyX doesn't know about that. Similarly, it's easy to imagine a case where a particular layout wants to prohibit a module from being used, and I think siamltex actually is such a case: Among the styles that the new 1.5 layout removes are several that are in the new module theorems-ams-extended, so we don't want to user to include it. (This is because siamltex is supposed to be highly specialized and faithful to the SIAM format.) But there's no facility for a layout to exclude a module at the moment.

So I think this really is needed, and it has to go in before 1.6 or wait for 1.7, since it is a (layout) format change.

Comments welcome.

Richard


Index: src/TextClass.h
===================================================================
--- src/TextClass.h	(revision 26925)
+++ src/TextClass.h	(working copy)
@@ -251,8 +251,12 @@
 	std::set<std::string> provides_;
 	/// latex packages requested by document class.
 	std::set<std::string> requires_;
-	/// modules wanted by document class
-	std::list<std::string> usemod_;
+	/// default modules wanted by document class
+	std::list<std::string> defmod_;
+	/// modules provided by document class
+	std::list<std::string> provmod_;
+	/// modules excluded by document class
+	std::list<std::string> exclmod_;
 	///
 	unsigned int columns_;
 	///
Index: src/TextClass.cpp
===================================================================
--- src/TextClass.cpp	(revision 26925)
+++ src/TextClass.cpp	(working copy)
@@ -61,7 +61,7 @@
 };
 
 
-int const FORMAT = 10;
+int const FORMAT = 11;
 
 
 bool layout2layout(FileName const & filename, FileName const & tempfile)
@@ -183,7 +183,9 @@
 	TC_TITLELATEXTYPE,
 	TC_FORMAT,
 	TC_ADDTOPREAMBLE,
-	TC_USEMODULE
+	TC_DEFAULTMODULE,
+	TC_PROVIDESMODULE,
+	TC_EXCLUDESMODULE
 };
 
 
@@ -195,7 +197,9 @@
 		{ "columns",         TC_COLUMNS },
 		{ "counter",         TC_COUNTER },
 		{ "defaultfont",     TC_DEFAULTFONT },
+		{ "defaultmodule",   TC_DEFAULTMODULE },
 		{ "defaultstyle",    TC_DEFAULTSTYLE },
+		{ "excludesmodule",  TC_EXCLUDESMODULE },
 		{ "float",           TC_FLOAT },
 		{ "format",          TC_FORMAT },
 		{ "input",           TC_INPUT },
@@ -207,6 +211,7 @@
 		{ "pagestyle",       TC_PAGESTYLE },
 		{ "preamble",        TC_PREAMBLE },
 		{ "provides",        TC_PROVIDES },
+		{ "providesmodule",  TC_PROVIDESMODULE },
 		{ "requires",        TC_REQUIRES },
 		{ "rightmargin",     TC_RIGHTMARGIN },
 		{ "secnumdepth",     TC_SECNUMDEPTH },
@@ -214,8 +219,7 @@
 		{ "style",           TC_STYLE },
 		{ "titlelatexname",  TC_TITLELATEXNAME },
 		{ "titlelatextype",  TC_TITLELATEXTYPE },
-		{ "tocdepth",        TC_TOCDEPTH },
-		{ "usemodule",       TC_USEMODULE }
+		{ "tocdepth",        TC_TOCDEPTH }
 	};
 	
 } //namespace anon
@@ -489,14 +493,38 @@
 			break;
 		}
 
-		case TC_USEMODULE: {
+		case TC_DEFAULTMODULE: {
 			lexrc.next();
 			string const module = lexrc.getString();
-			if (find(usemod_.begin(), usemod_.end(), module) == usemod_.end())
-				usemod_.push_back(module);
+			if (find(defmod_.begin(), defmod_.end(), module) == defmod_.end())
+				defmod_.push_back(module);
 			break;
 		}
 
+		case TC_PROVIDESMODULE: {
+			lexrc.next();
+			string const module = lexrc.getString();
+			if (rt == MODULE) {
+				LYXERR0("ProvidesModule tag cannot be used in a module!");
+				break;
+			}
+			if (find(provmod_.begin(), provmod_.end(), module) == provmod_.end())
+				provmod_.push_back(module);
+			break;
+		}
+
+		case TC_EXCLUDESMODULE: {
+			lexrc.next();
+			string const module = lexrc.getString();
+			if (rt == MODULE) {
+				LYXERR0("ExcludesModule tag cannot be used in a module!");
+				break;
+			}
+			if (find(exclmod_.begin(), exclmod_.end(), module) == exclmod_.end())
+				exclmod_.push_back(module);
+			break;
+		}
+
 		case TC_LEFTMARGIN:	// left margin type
 			if (lexrc.next())
 				leftmargin_ = lexrc.getDocString();
Index: src/LayoutFile.h
===================================================================
--- src/LayoutFile.h	(revision 26925)
+++ src/LayoutFile.h	(working copy)
@@ -65,7 +65,11 @@
 	bool isTeXClassAvailable() const { return texClassAvail_; }
 	///
 	std::list<std::string> const & defaultModules() const 
-			{ return usemod_; }
+			{ return defmod_; }
+ 	std::list<std::string> const & providedModules() const 
+ 			{ return provmod_; }
+ 	std::list<std::string> const & excludedModules() const 
+ 			{ return exclmod_; }
 private:
 	/// Construct a layout with default values. Actual values loaded later.
 	explicit LayoutFile(std::string const & filename,
Index: src/BufferParams.h
===================================================================
--- src/BufferParams.h	(revision 26925)
+++ src/BufferParams.h	(working copy)
@@ -346,6 +346,12 @@
 	void readRemovedModules(Lexer &);
 	///
 	void addDefaultModules();
+	/// checks for consistency among modules: makes sure requirements
+	/// are met, no modules exclude one another, etc, and resolves any
+	/// such conflicts, leaving us with a consistent collection.
+	/// \return true if modules were consistent, false if changes had
+	/// to be made.
+	bool checkModuleConsistency();
 
 	/// for use with natbib
 	CiteEngine cite_engine_;
Index: src/BufferParams.cpp
===================================================================
--- src/BufferParams.cpp	(revision 26925)
+++ src/BufferParams.cpp	(working copy)
@@ -1548,6 +1548,105 @@
 }
 
 
+bool BufferParams::checkModuleConsistency() {
+	bool consistent = true;
+		// Perform a consistency check on the set of modules.
+	// In particular, we need to check that modules provided by this class
+	// do not conflict with modules chosen by the user.
+	list<string> oldModules = getModules();
+	clearLayoutModules();
+	list<string>::const_iterator oit = oldModules.begin();
+	list<string>::const_iterator oen = oldModules.end();
+	list<string> const & provMods = baseClass()->providedModules();
+	list<string> const & exclMods = baseClass()->excludedModules();
+	for (; oit != oen; ++oit) {
+		string const & modname = *oit;
+		// skip modules that the class provides
+		if (find(provMods.begin(), provMods.end(), modname) != provMods.end()) {
+			consistent = false;
+			LYXERR0("Module " << modname << " dropped because provided by document class.");
+			continue;
+		}
+		// are we excluded by the document class?
+		if (find(exclMods.begin(), exclMods.end(), modname) != exclMods.end()) {
+			consistent = false;
+			LYXERR0("Module " << modname << " dropped because excluded by document class.");
+			continue;
+		}
+
+		// determine whether some provided module excludes us or we exclude it
+		list<string>::const_iterator pit = provMods.begin();
+		list<string>::const_iterator pen = provMods.end();
+		bool excluded = false;
+		for (; !excluded && pit != pen; ++pit) {
+			if (!LyXModule::areCompatible(modname, *pit)) {
+				consistent = false;
+				LYXERR0("Module " << modname << 
+						" dropped becuase it conflicts with provided module " << *pit);
+				excluded = true;
+			}
+		}
+
+		if (excluded)
+			continue;
+
+		// Determine whether some prior module excludes us, or we exclude it
+		list<string>::const_iterator lit = layoutModules_.begin();
+		list<string>::const_iterator len = layoutModules_.end();
+		for (; !excluded && lit != len; ++lit) {
+			if (!LyXModule::areCompatible(modname, *lit)) {
+				consistent = false;
+				LYXERR0("Module " << modname << 
+						" dropped because it is excluded by prior module " << *lit);
+				excluded = true;
+			}
+		}
+
+		if (excluded)
+			continue;
+
+		// determine whether some provided module or some prior module
+		// satisfies our requirements
+		LyXModule const * const oldmod = moduleList[modname];
+		if (!oldmod) {
+			LYXERR0("Default module " << modname << 
+					" added although it is unavailable and can't check requirements.");
+			continue;
+		}
+			
+		vector<string> const & reqs = oldmod->getRequiredModules();
+		if (!reqs.empty()) {
+			// we now set excluded to true, meaning that we haven't
+			// yet found a required module.
+			excluded = true;
+			vector<string>::const_iterator rit  = reqs.begin();
+			vector<string>::const_iterator ren = reqs.end();
+			for (; rit != ren; ++rit) {
+				string const reqmod = *rit;
+				if (find(provMods.begin(), provMods.end(), reqmod) != 
+						provMods.end()) {
+					excluded = false;
+					break;
+				}
+				if (find(layoutModules_.begin(), layoutModules_.end(), reqmod) != 
+						layoutModules_.end()) {
+					excluded = false;
+					break;
+				}
+			}
+		}
+		if (excluded) {
+			consistent = false;
+			LYXERR0("Module " << modname << " dropped because requirements not met.");
+		} else {
+			LYXERR(Debug::TCLASS, "Module " << modname << " passed consistency check.");
+			layoutModules_.push_back(modname);
+		}
+	}
+	return consistent;
+}
+
+
 bool BufferParams::setBaseClass(string const & classname)
 {
 	LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
@@ -1574,6 +1673,7 @@
 
 	pimpl_->baseClass_ = classname;
 	addDefaultModules();
+	checkModuleConsistency();
 
 	return true;
 }
@@ -1645,6 +1745,8 @@
 
 bool BufferParams::addLayoutModule(string const & modName) 
 {
+	// FIXME Need to check here that the module isn't excluded by 
+	// some already used module.
 	LayoutModuleList::const_iterator it = layoutModules_.begin();
 	LayoutModuleList::const_iterator end = layoutModules_.end();
 	for (; it != end; it++)
Index: src/frontends/qt4/GuiDocument.h
===================================================================
--- src/frontends/qt4/GuiDocument.h	(revision 26925)
+++ src/frontends/qt4/GuiDocument.h	(working copy)
@@ -181,7 +181,12 @@
 	std::list<modInfoStruct> const & getModuleInfo();
 	/// Modules in use in current buffer
 	std::list<modInfoStruct> const getSelectedModules();
+ 	///
+	std::list<modInfoStruct> const getProvidedModules();
 	///
+	std::list<modInfoStruct> const 
+			makeModuleInfo(std::list<std::string> const & mods);
+	///
 	void setLanguage() const;
 	///
 	void saveAsDefault() const;
Index: src/frontends/qt4/GuiDocument.cpp
===================================================================
--- src/frontends/qt4/GuiDocument.cpp	(revision 26925)
+++ src/frontends/qt4/GuiDocument.cpp	(working copy)
@@ -248,6 +248,12 @@
 	: GuiSelectionManager(availableLV, selectedLV, addPB, delPB,
                         upPB, downPB, availableModel, selectedModel)
 		{}
+	///
+	void updateProvidedModules(std::list<std::string> const & pm) 
+			{ providedModules_ = pm; }
+	///
+	void updateExcludedModules(std::list<std::string> const & em) 
+			{ excludedModules_ = em; }
 private:
 	///
 	virtual void updateAddPB();
@@ -267,6 +273,10 @@
 	{
 		return dynamic_cast<GuiIdListModel *>(selectedModel);
 	}
+	/// keeps a list of the modules the text class provides
+	std::list<std::string> providedModules_;
+	/// similarly...
+	std::list<std::string> excludedModules_;
 };
 
 void ModuleSelectionManager::updateAddPB() 
@@ -285,17 +295,26 @@
 	QModelIndex const & idx = availableLV->selectionModel()->currentIndex();
 	string const modName = getAvailableModel()->getIDString(idx.row());
 
+	// Is this module explicitly excluded by the document class?
+	list<string>::const_iterator const exclModStart = excludedModules_.begin();
+	list<string>::const_iterator const exclModEnd   = excludedModules_.end();
+	if (find(exclModStart, exclModEnd, modName) != exclModEnd) {
+		addPB->setEnabled(false);
+		return;
+	}
+
+	// Is this module already provided by the document class?
+	list<string>::const_iterator const provModStart = providedModules_.begin();
+	list<string>::const_iterator const provModEnd   = providedModules_.end();
+	if (find(provModStart, provModEnd, modName) != provModEnd) {
+		addPB->setEnabled(false);
+		return;
+	}
+
 	int const srows = selectedModel->rowCount();
-	// if no modules are yet selected, there is no more to check.
-	if (srows == 0) {
- 		addPB->setEnabled(true);
- 		return;
- 	}
-
 	vector<string> selModList;
 	for (int i = 0; i < srows; ++i)
 		selModList.push_back(getSelectedModel()->getIDString(i));
-
 	vector<string>::const_iterator const selModStart = selModList.begin();
 	vector<string>::const_iterator const selModEnd   = selModList.end();
 
@@ -306,7 +325,8 @@
 		vector<string>::const_iterator it = reqs.begin();
 		vector<string>::const_iterator en = reqs.end();
 		for (; it != en; ++it) {
-			if (find(selModStart, selModEnd, *it) != selModEnd) {
+			if (find(selModStart, selModEnd, *it) != selModEnd
+			    || find (provModStart, provModEnd, *it) != provModEnd) {
 				foundOne = true;
 				break;
 			}
@@ -318,8 +338,17 @@
 	}
 
 	// Check for conflicts with used modules
- 	vector<string>::const_iterator selModIt = selModStart;
- 	for (; selModIt != selModEnd; ++selModIt) {
+	// first the provided modules...
+	list<string>::const_iterator provModIt = provModStart;
+	for (; provModIt != provModEnd; ++provModIt) {
+		if (!LyXModule::areCompatible(modName, *provModIt)) {
+			addPB->setEnabled(false);
+			return;
+		}
+	}
+	// and then the selected modules
+	vector<string>::const_iterator selModIt = selModStart;
+	for (; selModIt != selModEnd; ++selModIt) {
 		if (!LyXModule::areCompatible(modName, *selModIt)) {
 			addPB->setEnabled(false);
 			return;
@@ -1369,8 +1398,6 @@
 	// class. So when we set the base class, we also need to recreate the document 
 	// class. Otherwise, we still have the old one.
 	bp_.makeDocumentClass();
-	// the new class may require some default modules.
-	updateSelectedModules();
 	paramsToDialog();
 }
 
@@ -1437,6 +1464,13 @@
 	string const modName = idModel.getIDString(idx.row());
 	docstring desc = getModuleDescription(modName);
 
+	list<string> const & provMods = bp_.baseClass()->providedModules();
+	if (std::find(provMods.begin(), provMods.end(), modName) != provMods.end()) {
+		if (!desc.empty())
+			desc += "\n";
+		desc += _("Module provided by document class.");
+	}
+
 	vector<string> pkgList = getPackageList(modName);
 	docstring pkgdesc = formatStrVec(pkgList, _("and"));
 	if (!pkgdesc.empty()) {
@@ -1606,9 +1640,13 @@
 	// Modules
 	params.clearLayoutModules();
 	int const srows = modules_sel_model_.rowCount();
-	vector<string> selModList;
-	for (int i = 0; i < srows; ++i)
-		params.addLayoutModule(modules_sel_model_.getIDString(i));
+	list<string> const & provMods = params.baseClass()->providedModules();
+	for (int i = 0; i < srows; ++i) {
+		string curmod = modules_sel_model_.getIDString(i);
+		// We add it only if it isn't a provided module
+		if (std::find(provMods.begin(), provMods.end(), curmod) == provMods.end())
+			params.addLayoutModule(curmod);
+	}
 	// update the list of removed modules
 	params.clearRemovedModules();
 	list<string> const & reqmods = params.baseClass()->defaultModules();
@@ -2018,6 +2056,11 @@
 	// latex
 	latexModule->defaultOptionsCB->setChecked(
 			bp_.use_default_options);
+	updateSelectedModules();
+	selectionManager->updateProvidedModules(
+			bp_.baseClass()->providedModules());
+	selectionManager->updateExcludedModules(
+			bp_.baseClass()->excludedModules());
 
 	if (!documentClass().options().empty()) {
 		latexModule->defaultOptionsLE->setText(
@@ -2270,7 +2313,6 @@
 	bp_ = view->buffer().params();
 	loadModuleInfo();
 	updateAvailableModules();
-	updateSelectedModules();
 	//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
@@ -2299,9 +2341,9 @@
 }
 
 
-list<GuiDocument::modInfoStruct> const GuiDocument::getSelectedModules()
+list<GuiDocument::modInfoStruct> const 
+		GuiDocument::makeModuleInfo(list<string> const & mods)
 {
-	list<string> const & mods = params().getModules();
 	list<string>::const_iterator it =  mods.begin();
 	list<string>::const_iterator end = mods.end();
 	list<modInfoStruct> mInfo;
@@ -2319,6 +2361,18 @@
 }
 
 
+list<GuiDocument::modInfoStruct> const GuiDocument::getSelectedModules()
+{
+	return makeModuleInfo(params().getModules());
+}
+
+
+list<GuiDocument::modInfoStruct> const GuiDocument::getProvidedModules()
+{
+	return makeModuleInfo(params().baseClass()->providedModules());
+}
+
+
 DocumentClass const & GuiDocument::documentClass() const
 {
 	return bp_.documentClass();
Index: lib/scripts/layout2layout.py
===================================================================
--- lib/scripts/layout2layout.py	(revision 26925)
+++ lib/scripts/layout2layout.py	(working copy)
@@ -36,9 +36,12 @@
 # Incremented to format 10, 6 October 2008 by rgh
 # Change format of counters
 
-currentFormat = 10
+# Incremented to format 11, 14 October 2008 by rgh
+# Add ProvidesModule, ExcludesModule tags
 
+currentFormat = 11
 
+
 def usage(prog_name):
     return ("Usage: %s inputfile outputfile\n" % prog_name +
             "or     %s <inputfile >outputfile" % prog_name)
@@ -98,6 +101,7 @@
     re_Comment = re.compile(r'^(\s*)#')
     re_Counter = re.compile(r'\s*Counter\s*', re.IGNORECASE)
     re_Name = re.compile(r'\s*Name\s+(\S+)\s*', re.IGNORECASE)
+    re_UseMod = re.compile(r'^\s*UseModule\s+(.*)', re.IGNORECASE)
     re_Empty = re.compile(r'^(\s*)$')
     re_Format = re.compile(r'^(\s*)(Format)(\s+)(\S+)', re.IGNORECASE)
     re_Preamble = re.compile(r'^(\s*)Preamble', re.IGNORECASE)
@@ -193,6 +197,14 @@
                 i += 1
             continue
 
+        if format == 10:
+            match = re_UseMod.match(lines[i])
+            if match:
+                module = match.group(1)
+                lines[i] = "DefaultModule " + module
+            i += 1
+            continue
+
         if format == 9:
             match = re_Counter.match(lines[i])
             if match:
Index: lib/layouts/siamltex.layout
===================================================================
--- lib/layouts/siamltex.layout	(revision 26925)
+++ lib/layouts/siamltex.layout	(working copy)
@@ -10,7 +10,7 @@
 # Modified from  amsart.layout May '08 by Andrew Corrigan <[EMAIL PROTECTED]>
 
 
-Format 10
+Format 11
 Columns                 1
 Sides                   2
 PageStyle               Headers
@@ -21,10 +21,11 @@
 	FontSize   8|9|10|11|12
 End
 
-# FIXME: this is not ideal, but we cannot
-# load the module regularly, because we 
-# have to disable some layouts (see below)
+# We need to load the module this way 
+# so we can disable some layouts below.
 Input theorems-ams.module
+ProvidesModule theorems-ams
+ExcludesModule theorems-ams-extended
 
 Style Standard
 	Category              MainText

Reply via email to