]> git.lyx.org Git - lyx.git/blob - src/TextClassList.cpp
LYX_CXX_GLOBAL_CSTD is not really useful anymore
[lyx.git] / src / TextClassList.cpp
1 /**
2  * \file TextClassList.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "TextClassList.h"
15 #include "TextClass.h"
16 #include "support/debug.h"
17 #include "Lexer.h"
18
19 #include "support/filetools.h"
20
21 #include <boost/bind.hpp>
22 #include <boost/regex.hpp>
23
24 #include <fstream>
25
26 using namespace std;
27 using namespace lyx::support;
28
29 namespace lyx {
30
31 using boost::bind;
32 using boost::regex;
33 using boost::smatch;
34
35 // Gets textclass number from name
36 pair<bool, textclass_type> const
37 TextClassList::numberOfClass(string const & textclass) const
38 {
39         ClassList::const_iterator cit =
40                 find_if(classlist_.begin(), classlist_.end(),
41                         bind(equal_to<string>(),
42                              bind(&TextClass::name, _1),
43                              textclass));
44
45         return cit != classlist_.end() ?
46                 make_pair(true, textclass_type(cit - classlist_.begin())) :
47                 make_pair(false, textclass_type(0));
48 }
49
50
51 // Gets a textclass structure from number
52 TextClass const &
53 TextClassList::operator[](textclass_type textclass) const
54 {
55         if (textclass >= classlist_.size())
56                 return classlist_[0];
57         
58         //FIXME I don't believe the following line is actually necessary (rgh)
59         classlist_[textclass].load();
60         return classlist_[textclass];
61 }
62
63
64 // used when sorting the textclass list.
65 class less_textclass_avail_desc
66         : public binary_function<TextClass, TextClass, int>
67 {
68 public:
69         int operator()(TextClass const & tc1,
70                        TextClass const & tc2) const
71         {
72                 // Ordering criteria:
73                 //   1. Availability of text class
74                 //   2. Description (lexicographic)
75
76                 return (tc1.isTeXClassAvailable() && !tc2.isTeXClassAvailable()) ||
77                         (tc1.isTeXClassAvailable() == tc2.isTeXClassAvailable() &&
78                          tc1.description() < tc2.description());
79         }
80 };
81
82
83 // Reads LyX textclass definitions according to textclass config file
84 bool TextClassList::read()
85 {
86         Lexer lex(0, 0);
87         FileName const real_file = libFileSearch("", "textclass.lst");
88         LYXERR(Debug::TCLASS, "Reading textclasses from `" << real_file << '\'');
89
90         if (real_file.empty()) {
91                 lyxerr << "TextClassList::Read: unable to find "
92                           "textclass file  `"
93                        << to_utf8(makeDisplayPath(real_file.absFilename(), 1000))
94                        << "'. Exiting." << endl;
95                 return false;
96                 // This causes LyX to end... Not a desirable behaviour. Lgb
97                 // What do you propose? That the user gets a file dialog
98                 // and is allowed to hunt for the file? (Asger)
99                 // more that we have a layout for minimal.cls statically
100                 // compiled in... (Lgb)
101         }
102
103         if (!lex.setFile(real_file)) {
104                 lyxerr << "TextClassList::Read: "
105                         "lyxlex was not able to set file: "
106                        << real_file << endl;
107         }
108
109         if (!lex.isOK()) {
110                 lyxerr << "TextClassList::Read: unable to open "
111                           "textclass file  `"
112                        << to_utf8(makeDisplayPath(real_file.absFilename(), 1000))
113                        << "'\nCheck your installation. LyX can't continue."
114                        << endl;
115                 return false;
116         }
117
118         bool finished = false;
119         // Parse config-file
120         LYXERR(Debug::TCLASS, "Starting parsing of textclass.lst");
121         while (lex.isOK() && !finished) {
122                 LYXERR(Debug::TCLASS, "\tline by line");
123                 switch (lex.lex()) {
124                 case Lexer::LEX_FEOF:
125                         finished = true;
126                         break;
127                 default:
128                         string const fname = lex.getString();
129                         LYXERR(Debug::TCLASS, "Fname: " << fname);
130                         if (lex.next()) {
131                                 string const clname = lex.getString();
132                                 LYXERR(Debug::TCLASS, "Clname: " << clname);
133                                 if (lex.next()) {
134                                         string const desc = lex.getString();
135                                         LYXERR(Debug::TCLASS, "Desc: " << desc);
136                                         if (lex.next()) {
137                                                 bool avail = lex.getBool();
138                                                 LYXERR(Debug::TCLASS, "Avail: " << avail);
139                                                 // This code is run when we have
140                                                 // fname, clname, desc, and avail
141                                                 TextClass tmpl(fname, clname, desc, avail);
142                                                 if (lyxerr.debugging(Debug::TCLASS)) {
143                                                         tmpl.load();
144                                                 }
145                                                 classlist_.push_back(tmpl);
146                                         }
147                                 }
148                         }
149                 }
150         }
151         LYXERR(Debug::TCLASS, "End of parsing of textclass.lst");
152
153         // lyx will start with an empty classlist_, but only reconfigure is allowed
154         // in this case. This gives users a second chance to configure lyx if
155         // initial configuration fails. (c.f. bug 2829)
156         if (classlist_.empty())
157                 lyxerr << "TextClassList::Read: no textclasses found!"
158                        << endl;
159         else 
160                 // Ok everything loaded ok, now sort the list.
161                 sort(classlist_.begin(), classlist_.end(), less_textclass_avail_desc());
162         return true;
163 }
164
165
166 void TextClassList::reset(textclass_type const textclass) {
167         if (textclass >= classlist_.size())
168                 return;
169         TextClass const & tc = classlist_[textclass];
170         TextClass tmpl(tc.name(), tc.latexname(), tc.description(), 
171                        tc.isTeXClassAvailable());
172         classlist_[textclass] = tmpl;
173 }
174
175
176 pair<bool, textclass_type> const
177 TextClassList::addTextClass(string const & textclass, string const & path)
178 {
179         // only check for textclass.layout file, .cls can be anywhere in $TEXINPUTS
180         // NOTE: latex class name is defined in textclass.layout, which can be different from textclass
181         FileName const layout_file(addName(path, textclass + ".layout"));
182         if (layout_file.exists()) {
183                 LYXERR(Debug::TCLASS, "Adding class " << textclass << " from directory " << path);
184                 // Read .layout file and get description, real latex classname etc
185                 //
186                 // This is a C++ version of function processLayoutFile in configure.py,
187                 // which uses the following regex
188                 //     \Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}
189                 ifstream ifs(layout_file.toFilesystemEncoding().c_str());
190                 static regex const reg("^#\\s*\\\\Declare(LaTeX|DocBook)Class\\s*"
191                         "(?:\\[([^,]*)(?:,.*)*\\])*\\s*\\{(.*)\\}\\s*");
192                 string line;
193                 while (getline(ifs, line)) {
194                         // look for the \DeclareXXXClass line
195                         smatch sub;
196                         if (regex_match(line, sub, reg)) {
197                                 // returns: whole string, classtype (not used here), first option, description
198                                 BOOST_ASSERT(sub.size()==4);
199                                 // now, create a TextClass with description containing path information
200                                 TextClass tmpl(textclass, sub.str(2)==""?textclass:sub.str(2),
201                                         sub.str(3) + " <" + path + ">", true);
202                                 if (lyxerr.debugging(Debug::TCLASS))
203                                         tmpl.load(path);
204                                 // Do not add this local TextClass to classlist_ if it has
205                                 // already been loaded by, for example, a master buffer.
206                                 pair<bool, lyx::textclass_type> pp =
207                                         textclasslist.numberOfClass(textclass);
208                                 // only layouts from the same directory are considered to be identical.
209                                 if (pp.first && classlist_[pp.second].description() == tmpl.description())
210                                         return pp;
211                                 classlist_.push_back(tmpl);
212                                 return make_pair(true, classlist_.size() - 1);
213                         }
214                 }
215         }
216         // If .layout is not in local directory, or an invalid layout is found, return false
217         return make_pair(false, textclass_type(0));
218 }
219
220
221 // Global variable: textclass table.
222 TextClassList textclasslist;
223
224
225 textclass_type defaultTextclass()
226 {
227         // We want to return the article class. if `first' is
228         // true in the returned pair, then `second' is the textclass
229         // number; if it is false, second is 0. In both cases, second
230         // is what we want.
231         return textclasslist.numberOfClass("article").second;
232 }
233
234
235
236 // Reads the style files
237 bool LyXSetStyle()
238 {
239         LYXERR(Debug::TCLASS, "LyXSetStyle: parsing configuration...");
240
241         if (!textclasslist.read()) {
242                 LYXERR(Debug::TCLASS, "LyXSetStyle: an error occured "
243                         "during parsing.\n             Exiting.");
244                 return false;
245         }
246
247         LYXERR(Debug::TCLASS, "LyXSetStyle: configuration parsed.");
248         return true;
249 }
250
251
252 } // namespace lyx