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