]> git.lyx.org Git - lyx.git/blob - src/LayoutModuleList.cpp
Avoid full metrics computation with Update:FitCursor
[lyx.git] / src / LayoutModuleList.cpp
1 // -*- C++ -*-
2 /**
3  * \file LayoutModuleList.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 Kimberly 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 string LayoutModuleList::asString() const
57 {
58         string mods;
59         for (auto const & mod : lml_)
60                 mods += mod + ',';
61         // remove trailing comma
62         if (!mods.empty())
63                 mods.pop_back();
64         return mods;
65 }
66
67
68 bool LayoutModuleList::moduleCanBeAdded(string const & modName,
69                 LayoutFile const * const lay) const
70 {
71         // Is the module already present?
72         const_iterator it = begin();
73         const_iterator const en = end();
74         for (; it != en; ++it)
75                 if (*it == modName)
76                         return false;
77
78         LyXModule const * const lm = theModuleList[modName];
79         if (!lm)
80                 return true;
81
82         // Does this module conflict with the document class or any loaded modules?
83         if (moduleConflicts(modName, lay))
84                 return false;
85
86         // Is this module already provided by the document class?
87         const_iterator const provmodstart = lay->providedModules().begin();
88         const_iterator const provmodend = lay->providedModules().end();
89         if (find(provmodstart, provmodend, modName) != provmodend)
90                 return false;
91
92         // Check whether some required module is available
93         vector<string> const reqs = lm->getRequiredModules();
94         if (reqs.empty())
95                 return true;
96
97         const_iterator mit = begin();
98         const_iterator const men = end();
99         vector<string>::const_iterator rit = reqs.begin();
100         vector<string>::const_iterator ren = reqs.end();
101         bool foundone = false;
102         for (; rit != ren; ++rit) {
103                 if (find(mit, men, *rit) != men ||
104                     find(provmodstart, provmodend, *rit) != provmodend) {
105                         foundone = true;
106                         break;
107                 }
108         }
109
110         return foundone;
111 }
112
113
114 bool LayoutModuleList::moduleConflicts(string const & modName,
115                                        LayoutFile const * const lay) const
116 {
117         // Is this module explicitly excluded by the document class?
118         const_iterator const exclmodstart = lay->excludedModules().begin();
119         const_iterator const exclmodend = lay->excludedModules().end();
120         if (find(exclmodstart, exclmodend, modName) != exclmodend)
121                 return true;
122         // Check for conflicts with used modules
123         // first the provided modules...
124         const_iterator provmodit = lay->providedModules().begin();
125         const_iterator const provmodend = lay->providedModules().end();
126         for (; provmodit != provmodend; ++provmodit) {
127                 if (!LyXModule::areCompatible(modName, *provmodit))
128                         return true;
129         }
130         // and then the selected modules
131         const_iterator mit = begin();
132         const_iterator const men = end();
133         for (; mit != men; ++mit)
134                 if (!LyXModule::areCompatible(modName, *mit))
135                         return true;
136         return false;
137 }
138
139
140 void LayoutModuleList::addDefaultModules(LayoutFile const * const lay,
141                 std::list<string> removedModules)
142 {
143         LayoutModuleList mods = lay->defaultModules();
144         const_iterator mit = mods.begin();
145         const_iterator const men = mods.end();
146
147         // We want to insert the default modules at the beginning of
148         // the list, but also to insert them in the correct order.
149         // The obvious thing to do would be to collect them and then
150         // insert them, but that doesn't work because a later default
151         // module may require an earlier one, and then the test below
152         //     moduleCanBeAdded(modname)
153         // will fail. So we have to do it a more complicated way.
154         iterator insertpos = begin();
155         int numinserts = 0;
156
157         for (; mit != men; ++mit) {
158                 string const & modName = *mit;
159                 // make sure the user hasn't removed it
160                 if (find(removedModules.begin(), removedModules.end(), modName) !=
161                     removedModules.end()) {
162                         LYXERR(Debug::TCLASS, "Default module `" << modName <<
163                                         "' not added because removed by user.");
164                         continue;
165                 }
166
167                 if (!moduleCanBeAdded(modName, lay)) {
168                         // FIXME This could be because it's already present, so we should
169                         // probably return something indicating that.
170                         LYXERR(Debug::TCLASS, "Default module `" << modName <<
171                                         "' could not be added.");
172                         continue;
173                 }
174                 LYXERR(Debug::TCLASS, "Default module `" << modName << "' added.");
175                 insert(insertpos, modName);
176                 // now we reset insertpos
177                 ++numinserts;
178                 insertpos = begin();
179                 advance(insertpos, numinserts);
180         }
181 }
182
183
184 bool LayoutModuleList::removeBadModules(LayoutFile const * const lay)
185 {
186         // we'll write a new list of modules, since we can't just remove them,
187         // as that would invalidate our iterators
188         LayoutModuleList oldModules = *this;
189         clear();
190
191         LayoutModuleList const & provmods = lay->providedModules();
192         LayoutModuleList const & exclmods = lay->excludedModules();
193         bool consistent = true; // set to false if we have to do anything
194
195         const_iterator oit = oldModules.begin();
196         const_iterator const oen = oldModules.end();
197         for (; oit != oen; ++oit) {
198                 string const & modname = *oit;
199                 // skip modules that the class provides
200                 if (find(provmods.begin(), provmods.end(), modname) != provmods.end()) {
201                         LYXERR0("Module `" << modname << "' dropped because provided by document class.");
202                         consistent = false;
203                         continue;
204                 }
205                 // are we excluded by the document class?
206                 if (find(exclmods.begin(), exclmods.end(), modname) != exclmods.end()) {
207                         LYXERR0("Module `" << modname << "' dropped because excluded by document class.");
208                         consistent = false;
209                         continue;
210                 }
211                 // determine whether some provided module excludes us or we exclude it
212                 const_iterator pit = provmods.begin();
213                 const_iterator const pen = provmods.end();
214                 bool excluded = false;
215                 for (; !excluded && pit != pen; ++pit) {
216                         if (!LyXModule::areCompatible(modname, *pit)) {
217                                 LYXERR0("Module " << modname <<
218                                                 " dropped because it conflicts with provided module `" << *pit << "'.");
219                                 consistent = false;
220                                 excluded = true;
221                         }
222                 }
223                 if (excluded)
224                         continue;
225                 push_back(modname);
226         }
227         return consistent;
228 }
229
230
231 // Perform a consistency check on the set of modules. We need to make
232 // sure that none of the modules exclude each other and that requires
233 // are satisfied.
234 bool LayoutModuleList::checkModuleConsistency(LayoutFile const * const lay)
235 {
236         bool consistent = true;
237         LayoutModuleList oldModules = *this;
238         clear();
239         const_iterator oit = oldModules.begin();
240         const_iterator const oen = oldModules.end();
241         LayoutModuleList const & provmods = lay->providedModules();
242         for (; oit != oen; ++oit) {
243                 string const & modname = *oit;
244                 bool excluded = false;
245                 // Determine whether some prior module excludes us, or we exclude it
246                 const_iterator lit = begin();
247                 const_iterator const len = end();
248                 for (; !excluded && lit != len; ++lit) {
249                         if (!LyXModule::areCompatible(modname, *lit)) {
250                                 consistent = false;
251                                 LYXERR0("Module " << modname <<
252                                                 " dropped because it is excluded by prior module " << *lit);
253                                 excluded = true;
254                         }
255                 }
256
257                 if (excluded)
258                         continue;
259
260                 // determine whether some provided module or some prior module
261                 // satisfies our requirements
262                 LyXModule const * const oldmod = theModuleList[modname];
263                 if (!oldmod) {
264                         LYXERR0("Default module " << modname <<
265                                         " added although it is unavailable and can't check requirements.");
266                         continue;
267                 }
268
269                 vector<string> const & reqs = oldmod->getRequiredModules();
270                 if (!reqs.empty()) {
271                         // we now set excluded to true, meaning that we haven't
272                         // yet found a required module.
273                         excluded = true;
274                         vector<string>::const_iterator rit  = reqs.begin();
275                         vector<string>::const_iterator const ren = reqs.end();
276                         for (; rit != ren; ++rit) {
277                                 string const reqmod = *rit;
278                                 if (find(provmods.begin(), provmods.end(), reqmod) !=
279                                                 provmods.end()) {
280                                         excluded = false;
281                                         break;
282                                 }
283                                 if (find(begin(), end(), reqmod) != end()) {
284                                         excluded = false;
285                                         break;
286                                 }
287                         }
288                 }
289                 if (excluded) {
290                         consistent = false;
291                         LYXERR0("Module " << modname << " dropped because requirements not met.");
292                 } else {
293                         LYXERR(Debug::TCLASS, "Module " << modname << " passed consistency check.");
294                         push_back(modname);
295                 }
296         }
297         return consistent;
298 }
299
300 } // namespace lyx