]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
These insets are NOT_EDITABLE.
[lyx.git] / src / TextClass.cpp
1 /**
2  * \file TextClass.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 Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "TextClass.h"
18
19 #include "LayoutFile.h"
20 #include "Color.h"
21 #include "Counters.h"
22 #include "Floating.h"
23 #include "FloatList.h"
24 #include "Layout.h"
25 #include "Lexer.h"
26 #include "Font.h"
27 #include "ModuleList.h"
28
29 #include "frontends/alert.h"
30
31 #include "support/lassert.h"
32 #include "support/debug.h"
33 #include "support/ExceptionMessage.h"
34 #include "support/FileName.h"
35 #include "support/filetools.h"
36 #include "support/gettext.h"
37 #include "support/lstrings.h"
38 #include "support/os.h"
39
40 #include <algorithm>
41 #include <fstream>
42 #include <sstream>
43
44 using namespace std;
45 using namespace lyx::support;
46
47 namespace lyx {
48
49 namespace {
50
51 class LayoutNamesEqual : public unary_function<Layout, bool> {
52 public:
53         LayoutNamesEqual(docstring const & name)
54                 : name_(name)
55         {}
56         bool operator()(Layout const & c) const
57         {
58                 return c.name() == name_;
59         }
60 private:
61         docstring name_;
62 };
63
64 // Keep the changes documented in the Customization manual. 
65 int const FORMAT = 14;
66
67
68 bool layout2layout(FileName const & filename, FileName const & tempfile)
69 {
70         FileName const script = libFileSearch("scripts", "layout2layout.py");
71         if (script.empty()) {
72                 LYXERR0("Could not find layout conversion "
73                           "script layout2layout.py.");
74                 return false;
75         }
76
77         ostringstream command;
78         command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
79                 << ' ' << quoteName(filename.toFilesystemEncoding())
80                 << ' ' << quoteName(tempfile.toFilesystemEncoding());
81         string const command_str = command.str();
82
83         LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
84
85         cmd_ret const ret = runCommand(command_str);
86         if (ret.first != 0) {
87                 LYXERR0("Could not run layout conversion script layout2layout.py.");
88                 return false;
89         }
90         return true;
91 }
92
93
94 std::string translateRT(TextClass::ReadType rt) 
95 {
96         switch (rt) {
97         case TextClass::BASECLASS:
98                 return "textclass";
99         case TextClass::MERGE:
100                 return "input file";
101         case TextClass::MODULE:
102                 return "module file";
103         case TextClass::VALIDATION:
104                 return "validation";
105         }
106         // shutup warning
107         return string();
108 }
109
110 } // namespace anon
111
112
113 // This string should not be translated here, 
114 // because it is a layout identifier.
115 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
116
117
118 InsetLayout DocumentClass::plain_insetlayout_;
119
120
121 /////////////////////////////////////////////////////////////////////////
122 //
123 // TextClass
124 //
125 /////////////////////////////////////////////////////////////////////////
126
127 TextClass::TextClass()
128 {
129         outputType_ = LATEX;
130         columns_ = 1;
131         sides_ = OneSide;
132         secnumdepth_ = 3;
133         tocdepth_ = 3;
134         pagestyle_ = "default";
135         defaultfont_ = sane_font;
136         opt_fontsize_ = "10|11|12";
137         opt_pagestyle_ = "empty|plain|headings|fancy";
138         titletype_ = TITLE_COMMAND_AFTER;
139         titlename_ = "maketitle";
140         loaded_ = false;
141         _("Plain Layout"); // a hack to make this translatable
142 }
143
144
145 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
146 {
147         LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
148         if (!lay.read(lexrc, *this)) {
149                 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
150                 return false;
151         }
152         // Resolve fonts
153         lay.resfont = lay.font;
154         lay.resfont.realize(defaultfont_);
155         lay.reslabelfont = lay.labelfont;
156         lay.reslabelfont.realize(defaultfont_);
157         return true; // no errors
158 }
159
160
161 enum TextClassTags {
162         TC_OUTPUTTYPE = 1,
163         TC_INPUT,
164         TC_STYLE,
165         TC_DEFAULTSTYLE,
166         TC_INSETLAYOUT,
167         TC_NOSTYLE,
168         TC_COLUMNS,
169         TC_SIDES,
170         TC_PAGESTYLE,
171         TC_DEFAULTFONT,
172         TC_SECNUMDEPTH,
173         TC_TOCDEPTH,
174         TC_CLASSOPTIONS,
175         TC_PREAMBLE,
176         TC_PROVIDES,
177         TC_REQUIRES,
178         TC_LEFTMARGIN,
179         TC_RIGHTMARGIN,
180         TC_FLOAT,
181         TC_COUNTER,
182         TC_NOFLOAT,
183         TC_TITLELATEXNAME,
184         TC_TITLELATEXTYPE,
185         TC_FORMAT,
186         TC_ADDTOPREAMBLE,
187         TC_DEFAULTMODULE,
188         TC_PROVIDESMODULE,
189         TC_EXCLUDESMODULE
190 };
191
192
193 namespace {
194
195         LexerKeyword textClassTags[] = {
196                 { "addtopreamble",   TC_ADDTOPREAMBLE },
197                 { "classoptions",    TC_CLASSOPTIONS },
198                 { "columns",         TC_COLUMNS },
199                 { "counter",         TC_COUNTER },
200                 { "defaultfont",     TC_DEFAULTFONT },
201                 { "defaultmodule",   TC_DEFAULTMODULE },
202                 { "defaultstyle",    TC_DEFAULTSTYLE },
203                 { "excludesmodule",  TC_EXCLUDESMODULE },
204                 { "float",           TC_FLOAT },
205                 { "format",          TC_FORMAT },
206                 { "input",           TC_INPUT },
207                 { "insetlayout",     TC_INSETLAYOUT },
208                 { "leftmargin",      TC_LEFTMARGIN },
209                 { "nofloat",         TC_NOFLOAT },
210                 { "nostyle",         TC_NOSTYLE },
211                 { "outputtype",      TC_OUTPUTTYPE },
212                 { "pagestyle",       TC_PAGESTYLE },
213                 { "preamble",        TC_PREAMBLE },
214                 { "provides",        TC_PROVIDES },
215                 { "providesmodule",  TC_PROVIDESMODULE },
216                 { "requires",        TC_REQUIRES },
217                 { "rightmargin",     TC_RIGHTMARGIN },
218                 { "secnumdepth",     TC_SECNUMDEPTH },
219                 { "sides",           TC_SIDES },
220                 { "style",           TC_STYLE },
221                 { "titlelatexname",  TC_TITLELATEXNAME },
222                 { "titlelatextype",  TC_TITLELATEXTYPE },
223                 { "tocdepth",        TC_TOCDEPTH }
224         };
225         
226 } //namespace anon
227
228
229 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
230 {
231         LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT);
232         FileName const tempfile = FileName::tempName("convert_layout");
233         bool success = layout2layout(filename, tempfile);
234         if (success)
235                 success = readWithoutConv(tempfile, rt) == OK;
236         tempfile.removeFile();
237         return success;
238 }
239
240
241 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
242 {
243         if (!filename.isReadableFile()) {
244                 lyxerr << "Cannot read layout file `" << filename << "'."
245                        << endl;
246                 return ERROR;
247         }
248
249         LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
250                 to_utf8(makeDisplayPath(filename.absFilename())));
251
252         // Define the plain layout used in table cells, ert, etc. Note that 
253         // we do this before loading any layout file, so that classes can 
254         // override features of this layout if they should choose to do so.
255         if (rt == BASECLASS && !hasLayout(plain_layout_))
256                 layoutlist_.push_back(createBasicLayout(plain_layout_));
257
258         Lexer lexrc(textClassTags);
259         lexrc.setFile(filename);
260         ReturnValues retval = read(lexrc, rt);
261         
262         LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
263                         to_utf8(makeDisplayPath(filename.absFilename())));
264
265         return retval;
266 }
267
268
269 bool TextClass::read(FileName const & filename, ReadType rt)
270 {
271         ReturnValues const retval = readWithoutConv(filename, rt);
272         if (retval != FORMAT_MISMATCH)
273                 return retval == OK;
274
275         bool const worx = convertLayoutFormat(filename, rt);
276         if (!worx) {
277                 LYXERR0 ("Unable to convert " << filename << 
278                         " to format " << FORMAT);
279                 return false;
280         }
281         return true;
282 }
283
284
285 bool TextClass::validate(std::string const & str)
286 {
287         TextClass tc;
288         return tc.read(str, VALIDATION);
289 }
290
291
292 bool TextClass::read(std::string const & str, ReadType rt) 
293 {
294         Lexer lexrc(textClassTags);
295         istringstream is(str);
296         lexrc.setStream(is);
297         ReturnValues retval = read(lexrc, rt);
298
299         if (retval != FORMAT_MISMATCH) 
300                 return retval == OK;
301
302         // write the layout string to a temporary file
303         FileName const tempfile = FileName::tempName("TextClass_read");
304         ofstream os(tempfile.toFilesystemEncoding().c_str());
305         if (!os) {
306                 LYXERR0("Unable to create temporary file");
307                 return false;
308         }
309         os << str;
310         os.close();
311
312         // now try to convert it
313         bool const worx = convertLayoutFormat(tempfile, rt);
314         if (!worx) {
315                 LYXERR0("Unable to convert internal layout information to format " 
316                         << FORMAT);
317         }
318         tempfile.removeFile();
319         return worx;
320 }
321
322
323 // Reads a textclass structure from file.
324 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt) 
325 {
326         bool error = !lexrc.isOK();
327
328         // Format of files before the 'Format' tag was introduced
329         int format = 1;
330
331         // parsing
332         while (lexrc.isOK() && !error) {
333                 int le = lexrc.lex();
334
335                 switch (le) {
336                 case Lexer::LEX_FEOF:
337                         continue;
338
339                 case Lexer::LEX_UNDEF:
340                         lexrc.printError("Unknown TextClass tag `$$Token'");
341                         error = true;
342                         continue;
343
344                 default:
345                         break;
346                 }
347
348                 switch (static_cast<TextClassTags>(le)) {
349
350                 case TC_FORMAT:
351                         if (lexrc.next())
352                                 format = lexrc.getInteger();
353                         break;
354
355                 case TC_OUTPUTTYPE:   // output type definition
356                         readOutputType(lexrc);
357                         break;
358
359                 case TC_INPUT: // Include file
360                         if (lexrc.next()) {
361                                 string const inc = lexrc.getString();
362                                 FileName tmp = libFileSearch("layouts", inc,
363                                                             "layout");
364
365                                 if (tmp.empty()) {
366                                         lexrc.printError("Could not find input file: " + inc);
367                                         error = true;
368                                 } else if (!read(tmp, MERGE)) {
369                                         lexrc.printError("Error reading input"
370                                                          "file: " + tmp.absFilename());
371                                         error = true;
372                                 }
373                         }
374                         break;
375
376                 case TC_DEFAULTSTYLE:
377                         if (lexrc.next()) {
378                                 docstring const name = from_utf8(subst(lexrc.getString(),
379                                                           '_', ' '));
380                                 defaultlayout_ = name;
381                         }
382                         break;
383
384                 case TC_STYLE: {
385                         if (!lexrc.next()) {
386                                 lexrc.printError("No name given for style: `$$Token'.");
387                                 error = true;
388                                 break;
389                         }
390                         docstring const name = from_utf8(subst(lexrc.getString(),
391                                                         '_', ' '));
392                         if (name.empty()) {
393                                 string s = "Could not read name for style: `$$Token' "
394                                         + lexrc.getString() + " is probably not valid UTF-8!";
395                                 lexrc.printError(s.c_str());
396                                 Layout lay;
397                                 // Since we couldn't read the name, we just scan the rest
398                                 // of the style and discard it.
399                                 error = !readStyle(lexrc, lay);
400                         } else if (hasLayout(name)) {
401                                 Layout & lay = operator[](name);
402                                 error = !readStyle(lexrc, lay);
403                         } else {
404                                 Layout layout;
405                                 layout.setName(name);
406                                 error = !readStyle(lexrc, layout);
407                                 if (!error)
408                                         layoutlist_.push_back(layout);
409
410                                 if (defaultlayout_.empty()) {
411                                         // We do not have a default layout yet, so we choose
412                                         // the first layout we encounter.
413                                         defaultlayout_ = name;
414                                 }
415                         }
416                         break;
417                 }
418
419                 case TC_NOSTYLE:
420                         if (lexrc.next()) {
421                                 docstring const style = from_utf8(subst(lexrc.getString(),
422                                                      '_', ' '));
423                                 if (!deleteLayout(style))
424                                         lyxerr << "Cannot delete style `"
425                                                << to_utf8(style) << '\'' << endl;
426                         }
427                         break;
428
429                 case TC_COLUMNS:
430                         if (lexrc.next())
431                                 columns_ = lexrc.getInteger();
432                         break;
433
434                 case TC_SIDES:
435                         if (lexrc.next()) {
436                                 switch (lexrc.getInteger()) {
437                                 case 1: sides_ = OneSide; break;
438                                 case 2: sides_ = TwoSides; break;
439                                 default:
440                                         lyxerr << "Impossible number of page"
441                                                 " sides, setting to one."
442                                                << endl;
443                                         sides_ = OneSide;
444                                         break;
445                                 }
446                         }
447                         break;
448
449                 case TC_PAGESTYLE:
450                         lexrc.next();
451                         pagestyle_ = rtrim(lexrc.getString());
452                         break;
453
454                 case TC_DEFAULTFONT:
455                         defaultfont_ = lyxRead(lexrc);
456                         if (!defaultfont_.resolved()) {
457                                 lexrc.printError("Warning: defaultfont should "
458                                                  "be fully instantiated!");
459                                 defaultfont_.realize(sane_font);
460                         }
461                         break;
462
463                 case TC_SECNUMDEPTH:
464                         lexrc.next();
465                         secnumdepth_ = lexrc.getInteger();
466                         break;
467
468                 case TC_TOCDEPTH:
469                         lexrc.next();
470                         tocdepth_ = lexrc.getInteger();
471                         break;
472
473                 // First step to support options
474                 case TC_CLASSOPTIONS:
475                         readClassOptions(lexrc);
476                         break;
477
478                 case TC_PREAMBLE:
479                         preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
480                         break;
481
482                 case TC_ADDTOPREAMBLE:
483                         preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
484                         break;
485
486                 case TC_PROVIDES: {
487                         lexrc.next();
488                         string const feature = lexrc.getString();
489                         lexrc.next();
490                         if (lexrc.getInteger())
491                                 provides_.insert(feature);
492                         else
493                                 provides_.erase(feature);
494                         break;
495                 }
496
497                 case TC_REQUIRES: {
498                         lexrc.eatLine();
499                         vector<string> const req 
500                                 = getVectorFromString(lexrc.getString());
501                         requires_.insert(req.begin(), req.end());
502                         break;
503                 }
504
505                 case TC_DEFAULTMODULE: {
506                         lexrc.next();
507                         string const module = lexrc.getString();
508                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
509                                 default_modules_.push_back(module);
510                         break;
511                 }
512
513                 case TC_PROVIDESMODULE: {
514                         lexrc.next();
515                         string const module = lexrc.getString();
516                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
517                                 provided_modules_.push_back(module);
518                         break;
519                 }
520
521                 case TC_EXCLUDESMODULE: {
522                         lexrc.next();
523                         string const module = lexrc.getString();
524                         // modules already have their own way to exclude other modules
525                         if (rt == MODULE) {
526                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
527                                 break;
528                         }
529                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
530                                 excluded_modules_.push_back(module);
531                         break;
532                 }
533
534                 case TC_LEFTMARGIN:     // left margin type
535                         if (lexrc.next())
536                                 leftmargin_ = lexrc.getDocString();
537                         break;
538
539                 case TC_RIGHTMARGIN:    // right margin type
540                         if (lexrc.next())
541                                 rightmargin_ = lexrc.getDocString();
542                         break;
543
544                 case TC_INSETLAYOUT: {
545                         if (!lexrc.next()) {
546                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
547                                 error = true;
548                                 break;
549                         }
550                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
551                         if (name.empty()) {
552                                 string s = "Could not read name for InsetLayout: `$$Token' "
553                                         + lexrc.getString() + " is probably not valid UTF-8!";
554                                 lexrc.printError(s.c_str());
555                                 InsetLayout il;
556                                 // Since we couldn't read the name, we just scan the rest
557                                 // of the style and discard it.
558                                 il.read(lexrc, *this);
559                                 error = true;
560                         } else if (hasInsetLayout(name)) {
561                                 InsetLayout & il = insetlayoutlist_[name];
562                                 error = !il.read(lexrc, *this);
563                         } else {
564                                 InsetLayout il;
565                                 il.setName(name);
566                                 error = !il.read(lexrc, *this);
567                                 if (!error)
568                                         insetlayoutlist_[name] = il;
569                         }
570                         break;
571                 }
572
573                 case TC_FLOAT:
574                         readFloat(lexrc);
575                         break;
576
577                 case TC_COUNTER:
578                         if (lexrc.next()) {
579                                 docstring const name = lexrc.getDocString();
580                                 if (name.empty()) {
581                                         string s = "Could not read name for counter: `$$Token' "
582                                                         + lexrc.getString() + " is probably not valid UTF-8!";
583                                         lexrc.printError(s.c_str());
584                                         Counter c;
585                                         // Since we couldn't read the name, we just scan the rest
586                                         // and discard it.
587                                         c.read(lexrc);
588                                 } else
589                                         error = !counters_.read(lexrc, name);
590                         }
591                         else {
592                                 lexrc.printError("No name given for style: `$$Token'.");
593                                 error = true;
594                         }
595                         break;
596
597                 case TC_TITLELATEXTYPE:
598                         readTitleType(lexrc);
599                         break;
600
601                 case TC_TITLELATEXNAME:
602                         if (lexrc.next())
603                                 titlename_ = lexrc.getString();
604                         break;
605
606                 case TC_NOFLOAT:
607                         if (lexrc.next()) {
608                                 string const nofloat = lexrc.getString();
609                                 floatlist_.erase(nofloat);
610                         }
611                         break;
612                 } // end of switch
613
614                 //Note that this is triggered the first time through the loop unless
615                 //we hit a format tag.
616                 if (format != FORMAT)
617                         break;
618         }
619
620         if (format != FORMAT)
621                 return FORMAT_MISMATCH;
622
623         if (rt != BASECLASS) 
624                 return (error ? ERROR : OK);
625
626         if (defaultlayout_.empty()) {
627                 LYXERR0("Error: Textclass '" << name_
628                                                 << "' is missing a defaultstyle.");
629                 error = true;
630         }
631                 
632         // Try to erase "stdinsets" from the provides_ set. 
633         // The
634         //   Provides stdinsets 1
635         // declaration simply tells us that the standard insets have been
636         // defined. (It's found in stdinsets.inc but could also be used in
637         // user-defined files.) There isn't really any such package. So we
638         // might as well go ahead and erase it.
639         // If we do not succeed, then it was not there, which means that
640         // the textclass did not provide the definitions of the standard
641         // insets. So we need to try to load them.
642         int erased = provides_.erase("stdinsets");
643         if (!erased) {
644                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
645
646                 if (tmp.empty()) {
647                         throw ExceptionMessage(WarningException, _("Missing File"),
648                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
649                         error = true;
650                 } else if (!read(tmp, MERGE)) {
651                         throw ExceptionMessage(WarningException, _("Corrupt File"),
652                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
653                         error = true;
654                 }
655         }
656
657         min_toclevel_ = Layout::NOT_IN_TOC;
658         max_toclevel_ = Layout::NOT_IN_TOC;
659         const_iterator lit = begin();
660         const_iterator len = end();
661         for (; lit != len; ++lit) {
662                 int const toclevel = lit->toclevel;
663                 if (toclevel != Layout::NOT_IN_TOC) {
664                         if (min_toclevel_ == Layout::NOT_IN_TOC)
665                                 min_toclevel_ = toclevel;
666                         else
667                                 min_toclevel_ = min(min_toclevel_, toclevel);
668                         max_toclevel_ = max(max_toclevel_, toclevel);
669                 }
670         }
671         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
672                 << ", maximum is " << max_toclevel_);
673
674         return (error ? ERROR : OK);
675 }
676
677
678 void TextClass::readTitleType(Lexer & lexrc)
679 {
680         LexerKeyword titleTypeTags[] = {
681                 { "commandafter", TITLE_COMMAND_AFTER },
682                 { "environment",  TITLE_ENVIRONMENT }
683         };
684
685         PushPopHelper pph(lexrc, titleTypeTags);
686
687         int le = lexrc.lex();
688         switch (le) {
689         case Lexer::LEX_UNDEF:
690                 lexrc.printError("Unknown output type `$$Token'");
691                 break;
692         case TITLE_COMMAND_AFTER:
693         case TITLE_ENVIRONMENT:
694                 titletype_ = static_cast<TitleLatexType>(le);
695                 break;
696         default:
697                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
698                 break;
699         }
700 }
701
702
703 void TextClass::readOutputType(Lexer & lexrc)
704 {
705         LexerKeyword outputTypeTags[] = {
706                 { "docbook",  DOCBOOK },
707                 { "latex",    LATEX },
708                 { "literate", LITERATE }
709         };
710
711         PushPopHelper pph(lexrc, outputTypeTags);
712
713         int le = lexrc.lex();
714         switch (le) {
715         case Lexer::LEX_UNDEF:
716                 lexrc.printError("Unknown output type `$$Token'");
717                 return;
718         case LATEX:
719         case DOCBOOK:
720         case LITERATE:
721                 outputType_ = static_cast<OutputType>(le);
722                 break;
723         default:
724                 LYXERR0("Unhandled value " << le);
725                 break;
726         }
727 }
728
729
730 void TextClass::readClassOptions(Lexer & lexrc)
731 {
732         enum {
733                 CO_FONTSIZE = 1,
734                 CO_PAGESTYLE,
735                 CO_OTHER,
736                 CO_HEADER,
737                 CO_END
738         };
739
740         LexerKeyword classOptionsTags[] = {
741                 {"end",       CO_END },
742                 {"fontsize",  CO_FONTSIZE },
743                 {"header",    CO_HEADER },
744                 {"other",     CO_OTHER },
745                 {"pagestyle", CO_PAGESTYLE }
746         };
747
748         lexrc.pushTable(classOptionsTags);
749         bool getout = false;
750         while (!getout && lexrc.isOK()) {
751                 int le = lexrc.lex();
752                 switch (le) {
753                 case Lexer::LEX_UNDEF:
754                         lexrc.printError("Unknown ClassOption tag `$$Token'");
755                         continue;
756                 default: break;
757                 }
758                 switch (le) {
759                 case CO_FONTSIZE:
760                         lexrc.next();
761                         opt_fontsize_ = rtrim(lexrc.getString());
762                         break;
763                 case CO_PAGESTYLE:
764                         lexrc.next();
765                         opt_pagestyle_ = rtrim(lexrc.getString());
766                         break;
767                 case CO_OTHER:
768                         lexrc.next();
769                         options_ = lexrc.getString();
770                         break;
771                 case CO_HEADER:
772                         lexrc.next();
773                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
774                         break;
775                 case CO_END:
776                         getout = true;
777                         break;
778                 }
779         }
780         lexrc.popTable();
781 }
782
783
784 void TextClass::readFloat(Lexer & lexrc)
785 {
786         enum {
787                 FT_TYPE = 1,
788                 FT_NAME,
789                 FT_PLACEMENT,
790                 FT_EXT,
791                 FT_WITHIN,
792                 FT_STYLE,
793                 FT_LISTNAME,
794                 FT_BUILTIN,
795                 FT_END
796         };
797
798         LexerKeyword floatTags[] = {
799                 { "end", FT_END },
800                 { "extension", FT_EXT },
801                 { "guiname", FT_NAME },
802                 { "latexbuiltin", FT_BUILTIN },
803                 { "listname", FT_LISTNAME },
804                 { "numberwithin", FT_WITHIN },
805                 { "placement", FT_PLACEMENT },
806                 { "style", FT_STYLE },
807                 { "type", FT_TYPE }
808         };
809
810         lexrc.pushTable(floatTags);
811
812         string type;
813         string placement;
814         string ext;
815         string within;
816         string style;
817         string name;
818         string listName;
819         bool builtin = false;
820
821         bool getout = false;
822         while (!getout && lexrc.isOK()) {
823                 int le = lexrc.lex();
824                 switch (le) {
825                 case Lexer::LEX_UNDEF:
826                         lexrc.printError("Unknown float tag `$$Token'");
827                         continue;
828                 default: break;
829                 }
830                 switch (le) {
831                 case FT_TYPE:
832                         lexrc.next();
833                         type = lexrc.getString();
834                         if (floatlist_.typeExist(type)) {
835                                 Floating const & fl = floatlist_.getType(type);
836                                 placement = fl.placement();
837                                 ext = fl.ext();
838                                 within = fl.within();
839                                 style = fl.style();
840                                 name = fl.name();
841                                 listName = fl.listName();
842                                 builtin = fl.builtin();
843                         } 
844                         break;
845                 case FT_NAME:
846                         lexrc.next();
847                         name = lexrc.getString();
848                         break;
849                 case FT_PLACEMENT:
850                         lexrc.next();
851                         placement = lexrc.getString();
852                         break;
853                 case FT_EXT:
854                         lexrc.next();
855                         ext = lexrc.getString();
856                         break;
857                 case FT_WITHIN:
858                         lexrc.next();
859                         within = lexrc.getString();
860                         if (within == "none")
861                                 within.erase();
862                         break;
863                 case FT_STYLE:
864                         lexrc.next();
865                         style = lexrc.getString();
866                         break;
867                 case FT_LISTNAME:
868                         lexrc.next();
869                         listName = lexrc.getString();
870                         break;
871                 case FT_BUILTIN:
872                         lexrc.next();
873                         builtin = lexrc.getBool();
874                         break;
875                 case FT_END:
876                         getout = true;
877                         break;
878                 }
879         }
880
881         // Here if have a full float if getout == true
882         if (getout) {
883                 Floating fl(type, placement, ext, within,
884                             style, name, listName, builtin);
885                 floatlist_.newFloat(fl);
886                 // each float has its own counter
887                 counters_.newCounter(from_ascii(type), from_ascii(within),
888                                       docstring(), docstring());
889                 // also define sub-float counters
890                 docstring const subtype = "sub-" + from_ascii(type);
891                 counters_.newCounter(subtype, from_ascii(type),
892                                       "\\alph{" + subtype + "}", docstring());
893         }
894
895         lexrc.popTable();
896 }
897
898
899 bool TextClass::hasLayout(docstring const & n) const
900 {
901         docstring const name = n.empty() ? defaultLayoutName() : n;
902
903         return find_if(layoutlist_.begin(), layoutlist_.end(),
904                        LayoutNamesEqual(name))
905                 != layoutlist_.end();
906 }
907
908
909 bool TextClass::hasInsetLayout(docstring const & n) const
910 {
911         if (n.empty()) 
912                 return false;
913         InsetLayouts::const_iterator it = insetlayoutlist_.begin();
914         InsetLayouts::const_iterator en = insetlayoutlist_.end();
915         for (; it != en; ++it)
916                 if (n == it->first)
917                         return true;
918         return false;
919 }
920
921
922 Layout const & TextClass::operator[](docstring const & name) const
923 {
924         LASSERT(!name.empty(), /**/);
925
926         const_iterator it = 
927                 find_if(begin(), end(), LayoutNamesEqual(name));
928
929         if (it == end()) {
930                 lyxerr << "We failed to find the layout '" << to_utf8(name)
931                        << "' in the layout list. You MUST investigate!"
932                        << endl;
933                 for (const_iterator cit = begin(); cit != end(); ++cit)
934                         lyxerr  << " " << to_utf8(cit->name()) << endl;
935
936                 // we require the name to exist
937                 LASSERT(false, /**/);
938         }
939
940         return *it;
941 }
942
943
944 Layout & TextClass::operator[](docstring const & name)
945 {
946         LASSERT(!name.empty(), /**/);
947
948         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
949
950         if (it == end()) {
951                 LYXERR0("We failed to find the layout '" << to_utf8(name)
952                        << "' in the layout list. You MUST investigate!");
953                 for (const_iterator cit = begin(); cit != end(); ++cit)
954                         LYXERR0(" " << to_utf8(cit->name()));
955
956                 // we require the name to exist
957                 LASSERT(false, /**/);
958         }
959
960         return *it;
961 }
962
963
964 bool TextClass::deleteLayout(docstring const & name)
965 {
966         if (name == defaultLayoutName() || name == plainLayoutName())
967                 return false;
968
969         LayoutList::iterator it =
970                 remove_if(layoutlist_.begin(), layoutlist_.end(),
971                           LayoutNamesEqual(name));
972
973         LayoutList::iterator end = layoutlist_.end();
974         bool const ret = (it != end);
975         layoutlist_.erase(it, end);
976         return ret;
977 }
978
979
980 // Load textclass info if not loaded yet
981 bool TextClass::load(string const & path) const
982 {
983         if (loaded_)
984                 return true;
985
986         // Read style-file, provided path is searched before the system ones
987         // If path is a file, it is loaded directly.
988         FileName layout_file(path);
989         if (!path.empty() && !layout_file.isReadableFile())
990                 layout_file = FileName(addName(path, name_ + ".layout"));
991         if (layout_file.empty() || !layout_file.exists())
992                 layout_file = libFileSearch("layouts", name_, "layout");
993         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
994
995         if (!loaded_) {
996                 lyxerr << "Error reading `"
997                        << to_utf8(makeDisplayPath(layout_file.absFilename()))
998                        << "'\n(Check `" << name_
999                        << "')\nCheck your installation and "
1000                         "try Options/Reconfigure..." << endl;
1001         }
1002
1003         return loaded_;
1004 }
1005
1006
1007 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1008 {
1009         if (!hasLayout(n))
1010                 layoutlist_.push_back(createBasicLayout(n, true));
1011 }
1012
1013
1014 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const 
1015 {
1016         // FIXME The fix for the InsetLayout part of 4812 would be here:
1017         // Add the InsetLayout to the document class if it is not found.
1018         docstring n = name;
1019         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1020         while (!n.empty()) {
1021                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1022                 if (cit != cen && cit->first == n)
1023                         return cit->second;
1024                 size_t i = n.find(':');
1025                 if (i == string::npos)
1026                         break;
1027                 n = n.substr(0, i);
1028         }
1029         return plain_insetlayout_;
1030 }
1031
1032
1033 docstring const & TextClass::defaultLayoutName() const
1034 {
1035         // This really should come from the actual layout... (Lgb)
1036         return defaultlayout_;
1037 }
1038
1039
1040 Layout const & TextClass::defaultLayout() const
1041 {
1042         return operator[](defaultLayoutName());
1043 }
1044
1045
1046 bool TextClass::isDefaultLayout(Layout const & layout) const 
1047 {
1048         return layout.name() == defaultLayoutName();
1049 }
1050
1051
1052 bool TextClass::isPlainLayout(Layout const & layout) const 
1053 {
1054         return layout.name() == plainLayoutName();
1055 }
1056
1057
1058 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1059 {
1060         static Layout * defaultLayout = NULL;
1061
1062         if (defaultLayout) {
1063                 defaultLayout->setUnknown(unknown);
1064                 defaultLayout->setName(name);
1065                 return *defaultLayout;
1066         }
1067
1068         static char const * s = "Margin Static\n"
1069                         "LatexType Paragraph\n"
1070                         "LatexName dummy\n"
1071                         "Align Block\n"
1072                         "AlignPossible Left, Right, Center\n"
1073                         "LabelType No_Label\n"
1074                         "End";
1075         istringstream ss(s);
1076         Lexer lex(textClassTags);
1077         lex.setStream(ss);
1078         defaultLayout = new Layout;
1079         defaultLayout->setUnknown(unknown);
1080         defaultLayout->setName(name);
1081         if (!readStyle(lex, *defaultLayout)) {
1082                 // The only way this happens is because the hardcoded layout above
1083                 // is wrong.
1084                 LASSERT(false, /**/);
1085         };
1086         return *defaultLayout;
1087 }
1088
1089 /////////////////////////////////////////////////////////////////////////
1090 //
1091 // DocumentClassBundle
1092 //
1093 /////////////////////////////////////////////////////////////////////////
1094
1095 DocumentClassBundle::~DocumentClassBundle()
1096 {
1097         for (size_t i = 0; i != documentClasses_.size(); ++i)
1098                 delete documentClasses_[i];
1099         documentClasses_.clear();
1100 }
1101
1102 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1103 {
1104         DocumentClass * dc = new DocumentClass(baseClass);
1105         documentClasses_.push_back(dc);
1106         return *documentClasses_.back();
1107 }
1108
1109
1110 DocumentClassBundle & DocumentClassBundle::get()
1111 {
1112         static DocumentClassBundle singleton; 
1113         return singleton; 
1114 }
1115
1116
1117 DocumentClass & DocumentClassBundle::makeDocumentClass(
1118                 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1119 {
1120         DocumentClass & doc_class = newClass(baseClass);
1121         LayoutModuleList::const_iterator it = modlist.begin();
1122         LayoutModuleList::const_iterator en = modlist.end();
1123         for (; it != en; it++) {
1124                 string const modName = *it;
1125                 LyXModule * lm = moduleList[modName];
1126                 if (!lm) {
1127                         docstring const msg =
1128                                                 bformat(_("The module %1$s has been requested by\n"
1129                                                 "this document but has not been found in the list of\n"
1130                                                 "available modules. If you recently installed it, you\n"
1131                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1132                         ExceptionMessage(WarningException,_("Module not available"),
1133                                         msg + _("Some layouts may not be available."));
1134                         continue;
1135                 }
1136                 if (!lm->isAvailable()) {
1137                         docstring const msg =
1138                                                 bformat(_("The module %1$s requires a package that is\n"
1139                                                 "not available in your LaTeX installation. LaTeX output\n"
1140                                                 "may not be possible.\n"), from_utf8(modName));
1141                         ExceptionMessage(WarningException, _("Package not available"), msg);
1142                 }
1143                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1144                 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1145                         docstring const msg =
1146                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1147                         throw ExceptionMessage(WarningException, _("Read Error"), msg);
1148                 }
1149         }
1150         return doc_class;
1151 }
1152
1153
1154 /////////////////////////////////////////////////////////////////////////
1155 //
1156 // DocumentClass
1157 //
1158 /////////////////////////////////////////////////////////////////////////
1159
1160 DocumentClass::DocumentClass(LayoutFile const & tc)
1161         : TextClass(tc)
1162 {}
1163
1164
1165 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1166 {
1167         LayoutList::const_iterator it  = layoutlist_.begin();
1168         LayoutList::const_iterator end = layoutlist_.end();
1169         for (; it != end; ++it)
1170                 if (it->latexname() == lay)
1171                         return true;
1172         return false;
1173 }
1174
1175
1176 bool DocumentClass::provides(string const & p) const
1177 {
1178         return provides_.find(p) != provides_.end();
1179 }
1180
1181
1182 bool DocumentClass::hasTocLevels() const
1183 {
1184         return min_toclevel_ != Layout::NOT_IN_TOC;
1185 }
1186
1187
1188 /////////////////////////////////////////////////////////////////////////
1189 //
1190 // PageSides
1191 //
1192 /////////////////////////////////////////////////////////////////////////
1193
1194 ostream & operator<<(ostream & os, PageSides p)
1195 {
1196         switch (p) {
1197         case OneSide:
1198                 os << '1';
1199                 break;
1200         case TwoSides:
1201                 os << '2';
1202                 break;
1203         }
1204         return os;
1205 }
1206
1207
1208 } // namespace lyx