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