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