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