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