]> git.lyx.org Git - lyx.git/blob - src/LayoutModuleList.cpp
Remove updateInfo() calls in favor of doing the relevant work
[lyx.git] / src / LayoutModuleList.cpp
1 // -*- C++ -*-
2 /**
3  * \file ModuleList.cpp
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Richard Heck
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "LayoutModuleList.h"
15
16 #include "LayoutFile.h"
17 #include "ModuleList.h"
18
19 #include "support/debug.h"
20
21 #include <algorithm>
22 #include <string>
23 #include <vector>
24
25 using namespace std;
26
27 namespace lyx {
28
29
30 // the previous document class may have loaded some modules that the
31 // new one excludes, and the new class may provide, etc, some that
32 // conflict with ones that were already loaded. So we need to go
33 // through the list and fix everything. I suppose there are various
34 // ways this could be done, but the following seems to work at the
35 // moment. (Thanks to Philippe Charpentier for helping work out all
36 // the bugs---rgh.)
37 bool LayoutModuleList::adaptToBaseClass(LayoutFile const * const lay,
38                 std::list<string> const & removedModules)
39 {
40         // first, we remove any modules the new document class itself provides,
41         // those it excludes, and those that conflict with ones it excludes.
42         // this has to be done first because, otherwise, a module we're about
43         // to remove could prevent a default module from being added.
44         bool retval = removeBadModules(lay);
45         // next, we add any default modules the new class provides.
46         addDefaultModules(lay, removedModules);
47         // finally, we perform a general consistency check on the set of
48         // loaded modules. it's a hassle that we have to do this now, since
49         // we just went through them a bit ago, but things might have changed
50         // with the loading of the default modules.
51         retval = (checkModuleConsistency(lay) || retval);
52         return retval;
53 }
54
55
56 bool LayoutModuleList::moduleCanBeAdded(string const & modName,
57                 LayoutFile const * const lay) const
58 {
59         // Is the module already present?
60         const_iterator it = begin();
61         const_iterator const en = end();
62         for (; it != en; ++it)
63                 if (*it == modName)
64                         return false;
65
66         LyXModule const * const lm = theModuleList[modName];
67         if (!lm)
68                 return true;
69
70         // Does this module conflict with the document class or any loaded modules?
71         if (moduleConflicts(modName, lay))
72                 return false;
73
74         // Is this module already provided by the document class?
75         const_iterator const provmodstart = lay->providedModules().begin();
76         const_iterator const provmodend = lay->providedModules().end();
77         if (find(provmodstart, provmodend, modName) != provmodend)
78                 return false;
79
80         // Check whether some required module is available
81         vector<string> const reqs = lm->getRequiredModules();
82         if (reqs.empty())
83                 return true;
84
85         const_iterator mit = begin();
86         const_iterator const men = end();
87         vector<string>::const_iterator rit = reqs.begin();
88         vector<string>::const_iterator ren = reqs.end();
89         bool foundone = false;
90         for (; rit != ren; ++rit) {
91                 if (find(mit, men, *rit) != men ||
92                     find(provmodstart, provmodend, *rit) != provmodend) {
93                         foundone = true;
94                         break;
95                 }
96         }
97
98         return foundone;
99 }
100
101
102 bool LayoutModuleList::moduleConflicts(string const & modName,
103                                        LayoutFile const * const lay) const
104 {
105         // Is this module explicitly excluded by the document class?
106         const_iterator const exclmodstart = lay->excludedModules().begin();
107         const_iterator const exclmodend = lay->excludedModules().end();
108         if (find(exclmodstart, exclmodend, modName) != exclmodend)
109                 return true;
110         // Check for conflicts with used modules
111         // first the provided modules...
112         const_iterator provmodit = lay->providedModules().begin();
113         const_iterator const provmodend = lay->providedModules().end();
114         for (; provmodit != provmodend; ++provmodit) {
115                 if (!LyXModule::areCompatible(modName, *provmodit))
116                         return true;
117         }
118         // and then the selected modules
119         const_iterator mit = begin();
120         const_iterator const men = end();
121         for (; mit != men; ++mit)
122                 if (!LyXModule::areCompatible(modName, *mit))
123                         return true;
124         return false;
125 }
126
127
128 void LayoutModuleList::addDefaultModules(LayoutFile const * const lay,
129                 std::list<string> removedModules)
130 {
131         LayoutModuleList mods = lay->defaultModules();
132         const_iterator mit = mods.begin();
133         const_iterator const men = mods.end();
134
135         // We want to insert the default modules at the beginning of
136         // the list, but also to insert them in the correct order.
137         // The obvious thing to do would be to collect them and then
138         // insert them, but that doesn't work because a later default
139         // module may require an earlier one, and then the test below
140         //     moduleCanBeAdded(modname)
141         // will fail. So we have to do it a more complicated way.
142         iterator insertpos = begin();
143         int numinserts = 0;
144
145         for (; mit != men; ++mit) {
146                 string const & modName = *mit;
147                 // make sure the user hasn't removed it
148                 if (find(removedModules.begin(), removedModules.end(), modName) !=
149                     removedModules.end()) {
150                         LYXERR(Debug::TCLASS, "Default module `" << modName <<
151                                         "' not added because removed by user.");
152                         continue;
153                 }
154
155                 if (!moduleCanBeAdded(modName, lay)) {
156                         // FIXME This could be because it's already present, so we should
157                         // probably return something indicating that.
158                         LYXERR(Debug::TCLASS, "Default module `" << modName <<
159                                         "' could not be added.");
160                         continue;
161                 }
162                 LYXERR(Debug::TCLASS, "Default module `" << modName << "' added.");
163                 insert(insertpos, modName);
164                 // now we reset insertpos
165                 ++numinserts;
166                 insertpos = begin();
167                 advance(insertpos, numinserts);
168         }
169 }
170
171
172 bool LayoutModuleList::removeBadModules(LayoutFile const * const lay)
173 {
174         // we'll write a new list of modules, since we can't just remove them,
175         // as that would invalidate our iterators
176         LayoutModuleList oldModules = *this;
177         clear();
178
179         LayoutModuleList const & provmods = lay->providedModules();
180         LayoutModuleList const & exclmods = lay->excludedModules();
181         bool consistent = true; // set to false if we have to do anything
182
183         const_iterator oit = oldModules.begin();
184         const_iterator const oen = oldModules.end();
185         for (; oit != oen; ++oit) {
186                 string const & modname = *oit;
187                 // skip modules that the class provides
188                 if (find(provmods.begin(), provmods.end(), modname) != provmods.end()) {
189                         LYXERR0("Module `" << modname << "' dropped because provided by document class.");
190                         consistent = false;
191                         continue;
192                 }
193                 // are we excluded by the document class?
194                 if (find(exclmods.begin(), exclmods.end(), modname) != exclmods.end()) {
195                         LYXERR0("Module `" << modname << "' dropped because excluded by document class.");
196                         consistent = false;
197                         continue;
198                 }
199                 // determine whether some provided module excludes us or we exclude it
200                 const_iterator pit = provmods.begin();
201                 const_iterator const pen = provmods.end();
202                 bool excluded = false;
203                 for (; !excluded && pit != pen; ++pit) {
204                         if (!LyXModule::areCompatible(modname, *pit)) {
205                                 LYXERR0("Module " << modname <<
206                                                 " dropped because it conflicts with provided module `" << *pit << "'.");
207                                 consistent = false;
208                                 excluded = true;
209                         }
210                 }
211                 if (excluded)
212                         continue;
213                 push_back(modname);
214         }
215         return consistent;
216 }
217
218
219 // Perform a consistency check on the set of modules. We need to make
220 // sure that none of the modules exclude each other and that requires
221 // are satisfied.
222 bool LayoutModuleList::checkModuleConsistency(LayoutFile const * const lay)
223 {
224         bool consistent = true;
225         LayoutModuleList oldModules = *this;
226         clear();
227         const_iterator oit = oldModules.begin();
228         const_iterator const oen = oldModules.end();
229         LayoutModuleList const & provmods = lay->providedModules();
230         for (; oit != oen; ++oit) {
231                 string const & modname = *oit;
232                 bool excluded = false;
233                 // Determine whether some prior module excludes us, or we exclude it
234                 const_iterator lit = begin();
235                 const_iterator const len = end();
236                 for (; !excluded && lit != len; ++lit) {
237                         if (!LyXModule::areCompatible(modname, *lit)) {
238                                 consistent = false;
239                                 LYXERR0("Module " << modname <<
240                                                 " dropped because it is excluded by prior module " << *lit);
241                                 excluded = true;
242                         }
243                 }
244
245                 if (excluded)
246                         continue;
247
248                 // determine whether some provided module or some prior module
249                 // satisfies our requirements
250                 LyXModule const * const oldmod = theModuleList[modname];
251                 if (!oldmod) {
252                         LYXERR0("Default module " << modname <<
253                                         " added although it is unavailable and can't check requirements.");
254                         continue;
255                 }
256
257                 vector<string> const & reqs = oldmod->getRequiredModules();
258                 if (!reqs.empty()) {
259                         // we now set excluded to true, meaning that we haven't
260                         // yet found a required module.
261                         excluded = true;
262                         vector<string>::const_iterator rit  = reqs.begin();
263                         vector<string>::const_iterator const ren = reqs.end();
264                         for (; rit != ren; ++rit) {
265                                 string const reqmod = *rit;
266                                 if (find(provmods.begin(), provmods.end(), reqmod) !=
267                                                 provmods.end()) {
268                                         excluded = false;
269                                         break;
270                                 }
271                                 if (find(begin(), end(), reqmod) != end()) {
272                                         excluded = false;
273                                         break;
274                                 }
275                         }
276                 }
277                 if (excluded) {
278                         consistent = false;
279                         LYXERR0("Module " << modname << " dropped because requirements not met.");
280                 } else {
281                         LYXERR(Debug::TCLASS, "Module " << modname << " passed consistency check.");
282                         push_back(modname);
283                 }
284         }
285         return consistent;
286 }
287
288 } // namespace lyx