]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Tweaks.
[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 "Color.h"
20 #include "Counters.h"
21 #include "support/debug.h"
22 #include "support/gettext.h"
23 #include "Floating.h"
24 #include "FloatList.h"
25 #include "Layout.h"
26 #include "Lexer.h"
27 #include "Font.h"
28
29 #include "frontends/alert.h"
30
31 #include "support/lstrings.h"
32 #include "support/FileName.h"
33 #include "support/filetools.h"
34 #include "support/os.h"
35
36 #include <sstream>
37
38 using namespace std;
39 using namespace lyx::support;
40
41 namespace lyx {
42
43 namespace {
44
45 class LayoutNamesEqual : public unary_function<LayoutPtr, bool> {
46 public:
47         LayoutNamesEqual(docstring const & name)
48                 : name_(name)
49         {}
50         bool operator()(LayoutPtr const & c) const
51         {
52                 return c->name() == name_;
53         }
54 private:
55         docstring name_;
56 };
57
58
59 int const FORMAT = 6;
60
61
62 bool layout2layout(FileName const & filename, FileName const & tempfile)
63 {
64         FileName const script = libFileSearch("scripts", "layout2layout.py");
65         if (script.empty()) {
66                 lyxerr << "Could not find layout conversion "
67                           "script layout2layout.py." << endl;
68                 return false;
69         }
70
71         ostringstream command;
72         command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
73                 << ' ' << quoteName(filename.toFilesystemEncoding())
74                 << ' ' << quoteName(tempfile.toFilesystemEncoding());
75         string const command_str = command.str();
76
77         LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
78
79         cmd_ret const ret =
80                 runCommand(command_str);
81         if (ret.first != 0) {
82                 lyxerr << "Could not run layout conversion "
83                           "script layout2layout.py." << endl;
84                 return false;
85         }
86         return true;
87 }
88
89
90 std::string translateRT(TextClass::ReadType rt) 
91 {
92         switch (rt) {
93         case TextClass::BASECLASS:
94                 return "textclass";
95         case TextClass::MERGE:
96                 return "input file";
97         case TextClass::MODULE:
98                 return "module file";
99         }
100 }
101
102 } // namespace anon
103
104
105 TextClass::TextClass(string const & fn, string const & cln,
106                            string const & desc, bool texClassAvail )
107         : name_(fn), latexname_(cln), description_(desc),
108           floatlist_(new FloatList), counters_(new Counters),
109           texClassAvail_(texClassAvail)
110 {
111         modular_ = false;
112         outputType_ = LATEX;
113         columns_ = 1;
114         sides_ = OneSide;
115         secnumdepth_ = 3;
116         tocdepth_ = 3;
117         pagestyle_ = "default";
118         defaultfont_ = sane_font;
119         opt_fontsize_ = "10|11|12";
120         opt_pagestyle_ = "empty|plain|headings|fancy";
121         titletype_ = TITLE_COMMAND_AFTER;
122         titlename_ = "maketitle";
123         loaded_ = false;
124 }
125
126
127 bool TextClass::isTeXClassAvailable() const
128 {
129         return texClassAvail_;
130 }
131
132
133 bool TextClass::readStyle(Lexer & lexrc, Layout & lay)
134 {
135         LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
136         if (!lay.read(lexrc, *this)) {
137                 // Resolve fonts
138                 lay.resfont = lay.font;
139                 lay.resfont.realize(defaultfont());
140                 lay.reslabelfont = lay.labelfont;
141                 lay.reslabelfont.realize(defaultfont());
142                 return false; // no errors
143         }
144         lyxerr << "Error parsing style `" << to_utf8(lay.name()) << '\'' << endl;
145         return true;
146 }
147
148
149 enum TextClassTags {
150         TC_OUTPUTTYPE = 1,
151         TC_INPUT,
152         TC_STYLE,
153         TC_DEFAULTSTYLE,
154         TC_INSETLAYOUT,
155         TC_ENVIRONMENT,
156         TC_NOSTYLE,
157         TC_COLUMNS,
158         TC_SIDES,
159         TC_PAGESTYLE,
160         TC_DEFAULTFONT,
161         TC_SECNUMDEPTH,
162         TC_TOCDEPTH,
163         TC_CLASSOPTIONS,
164         TC_PREAMBLE,
165         TC_PROVIDES,
166         TC_REQUIRES,
167         TC_LEFTMARGIN,
168         TC_RIGHTMARGIN,
169         TC_FLOAT,
170         TC_COUNTER,
171         TC_NOFLOAT,
172         TC_TITLELATEXNAME,
173         TC_TITLELATEXTYPE,
174         TC_FORMAT
175 };
176
177
178 // Reads a textclass structure from file.
179 bool TextClass::read(FileName const & filename, ReadType rt)
180 {
181         if (!filename.isReadableFile()) {
182                 lyxerr << "Cannot read layout file `" << filename << "'."
183                        << endl;
184                 return true;
185         }
186
187         keyword_item textClassTags[] = {
188                 { "classoptions",    TC_CLASSOPTIONS },
189                 { "columns",         TC_COLUMNS },
190                 { "counter",         TC_COUNTER },
191                 { "defaultfont",     TC_DEFAULTFONT },
192                 { "defaultstyle",    TC_DEFAULTSTYLE },
193                 { "environment",     TC_ENVIRONMENT },
194                 { "float",           TC_FLOAT },
195                 { "format",          TC_FORMAT },
196                 { "input",           TC_INPUT },
197                 { "insetlayout",     TC_INSETLAYOUT },
198                 { "leftmargin",      TC_LEFTMARGIN },
199                 { "nofloat",         TC_NOFLOAT },
200                 { "nostyle",         TC_NOSTYLE },
201                 { "outputtype",      TC_OUTPUTTYPE },
202                 { "pagestyle",       TC_PAGESTYLE },
203                 { "preamble",        TC_PREAMBLE },
204                 { "provides",        TC_PROVIDES },
205                 { "requires",        TC_REQUIRES },
206                 { "rightmargin",     TC_RIGHTMARGIN },
207                 { "secnumdepth",     TC_SECNUMDEPTH },
208                 { "sides",           TC_SIDES },
209                 { "style",           TC_STYLE },
210                 { "titlelatexname",  TC_TITLELATEXNAME },
211                 { "titlelatextype",  TC_TITLELATEXTYPE },
212                 { "tocdepth",        TC_TOCDEPTH }
213         };
214
215         LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
216                 to_utf8(makeDisplayPath(filename.absFilename())));
217
218         Lexer lexrc(textClassTags,
219                 sizeof(textClassTags) / sizeof(textClassTags[0]));
220
221         lexrc.setFile(filename);
222         bool error = !lexrc.isOK();
223
224         // Format of files before the 'Format' tag was introduced
225         int format = 1;
226
227         // parsing
228         while (lexrc.isOK() && !error) {
229                 int le = lexrc.lex();
230
231                 switch (le) {
232                 case Lexer::LEX_FEOF:
233                         continue;
234
235                 case Lexer::LEX_UNDEF:
236                         lexrc.printError("Unknown TextClass tag `$$Token'");
237                         error = true;
238                         continue;
239
240                 default:
241                         break;
242                 }
243
244                 switch (static_cast<TextClassTags>(le)) {
245
246                 case TC_FORMAT:
247                         if (lexrc.next())
248                                 format = lexrc.getInteger();
249                         break;
250
251                 case TC_OUTPUTTYPE:   // output type definition
252                         readOutputType(lexrc);
253                         break;
254
255                 case TC_INPUT: // Include file
256                         if (lexrc.next()) {
257                                 string const inc = lexrc.getString();
258                                 FileName tmp = libFileSearch("layouts", inc,
259                                                             "layout");
260
261                                 if (tmp.empty()) {
262                                         lexrc.printError("Could not find input file: " + inc);
263                                         error = true;
264                                 } else if (read(tmp, MERGE)) {
265                                         lexrc.printError("Error reading input"
266                                                          "file: " + tmp.absFilename());
267                                         error = true;
268                                 }
269                         }
270                         break;
271
272                 case TC_DEFAULTSTYLE:
273                         if (lexrc.next()) {
274                                 docstring const name = from_utf8(subst(lexrc.getString(),
275                                                           '_', ' '));
276                                 defaultlayout_ = name;
277                         }
278                         break;
279
280                 case TC_ENVIRONMENT:
281                 case TC_STYLE:
282                         if (lexrc.next()) {
283                                 docstring const name = from_utf8(subst(lexrc.getString(),
284                                                     '_', ' '));
285                                 if (name.empty()) {
286                                         string s = "Could not read name for style: `$$Token' "
287                                                 + lexrc.getString() + " is probably not valid UTF-8!";
288                                         lexrc.printError(s.c_str());
289                                         Layout lay;
290                                         error = readStyle(lexrc, lay);
291                                 } else if (hasLayout(name)) {
292                                         Layout * lay = operator[](name).get();
293                                         error = readStyle(lexrc, *lay);
294                                 } else {
295                                         Layout lay;
296                                         lay.setName(name);
297                                         if (le == TC_ENVIRONMENT)
298                                                 lay.is_environment = true;
299                                         error = readStyle(lexrc, lay);
300                                         if (!error)
301                                                 layoutlist_.push_back(
302                                                         boost::shared_ptr<Layout>(new Layout(lay))
303                                                         );
304
305                                         if (defaultlayout_.empty()) {
306                                                 // We do not have a default
307                                                 // layout yet, so we choose
308                                                 // the first layout we
309                                                 // encounter.
310                                                 defaultlayout_ = name;
311                                         }
312                                 }
313                         }
314                         else {
315                                 lexrc.printError("No name given for style: `$$Token'.");
316                                 error = true;
317                         }
318                         break;
319
320                 case TC_NOSTYLE:
321                         if (lexrc.next()) {
322                                 docstring const style = from_utf8(subst(lexrc.getString(),
323                                                      '_', ' '));
324                                 if (!deleteLayout(style))
325                                         lyxerr << "Cannot delete style `"
326                                                << to_utf8(style) << '\'' << endl;
327 //                                      lexrc.printError("Cannot delete style"
328 //                                                       " `$$Token'");
329                         }
330                         break;
331
332                 case TC_COLUMNS:
333                         if (lexrc.next())
334                                 columns_ = lexrc.getInteger();
335                         break;
336
337                 case TC_SIDES:
338                         if (lexrc.next()) {
339                                 switch (lexrc.getInteger()) {
340                                 case 1: sides_ = OneSide; break;
341                                 case 2: sides_ = TwoSides; break;
342                                 default:
343                                         lyxerr << "Impossible number of page"
344                                                 " sides, setting to one."
345                                                << endl;
346                                         sides_ = OneSide;
347                                         break;
348                                 }
349                         }
350                         break;
351
352                 case TC_PAGESTYLE:
353                         lexrc.next();
354                         pagestyle_ = rtrim(lexrc.getString());
355                         break;
356
357                 case TC_DEFAULTFONT:
358                         defaultfont_ = lyxRead(lexrc);
359                         if (!defaultfont_.resolved()) {
360                                 lexrc.printError("Warning: defaultfont should "
361                                                  "be fully instantiated!");
362                                 defaultfont_.realize(sane_font);
363                         }
364                         break;
365
366                 case TC_SECNUMDEPTH:
367                         lexrc.next();
368                         secnumdepth_ = lexrc.getInteger();
369                         break;
370
371                 case TC_TOCDEPTH:
372                         lexrc.next();
373                         tocdepth_ = lexrc.getInteger();
374                         break;
375
376                         // First step to support options
377                 case TC_CLASSOPTIONS:
378                         readClassOptions(lexrc);
379                         break;
380
381                 case TC_PREAMBLE:
382                         preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
383                         break;
384
385                 case TC_PROVIDES: {
386                         lexrc.next();
387                         string const feature = lexrc.getString();
388                         lexrc.next();
389                         if (lexrc.getInteger())
390                                 provides_.insert(feature);
391                         else
392                                 provides_.erase(feature);
393                         break;
394                 }
395
396                 case TC_REQUIRES: {
397                         lexrc.eatLine();
398                         vector<string> const req 
399                                 = getVectorFromString(lexrc.getString());
400                         requires_.insert(req.begin(), req.end());
401                         break;
402                 }
403
404                 case TC_LEFTMARGIN:     // left margin type
405                         if (lexrc.next())
406                                 leftmargin_ = lexrc.getDocString();
407                         break;
408
409                 case TC_RIGHTMARGIN:    // right margin type
410                         if (lexrc.next())
411                                 rightmargin_ = lexrc.getDocString();
412                         break;
413                 case TC_INSETLAYOUT:
414                         if (lexrc.next()) {
415                                 docstring const name = subst(lexrc.getDocString(), '_', ' ');
416                                 readInsetLayout(lexrc, name);
417                         }
418                         break;
419                 case TC_FLOAT:
420                         readFloat(lexrc);
421                         break;
422                 case TC_COUNTER:
423                         readCounter(lexrc);
424                         break;
425                 case TC_TITLELATEXTYPE:
426                         readTitleType(lexrc);
427                         break;
428                 case TC_TITLELATEXNAME:
429                         if (lexrc.next())
430                                 titlename_ = lexrc.getString();
431                         break;
432                 case TC_NOFLOAT:
433                         if (lexrc.next()) {
434                                 string const nofloat = lexrc.getString();
435                                 floatlist_->erase(nofloat);
436                         }
437                         break;
438                 }
439                 if (format != FORMAT)
440                         break;
441         }
442
443         if (format != FORMAT) {
444                 LYXERR(Debug::TCLASS, "Converting layout file from format "
445                                       << format << " to " << FORMAT);
446                 FileName const tempfile = FileName::tempName();
447                 error = !layout2layout(filename, tempfile);
448                 if (!error)
449                         error = read(tempfile, rt);
450                 tempfile.removeFile();
451                 return error;
452         }
453
454         LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
455                         to_utf8(makeDisplayPath(filename.absFilename())));
456
457         if (rt == BASECLASS) {
458                 if (defaultlayout_.empty()) {
459                         lyxerr << "Error: Textclass '" << name_
460                                << "' is missing a defaultstyle." << endl;
461                         error = true;
462                 }
463
464                 min_toclevel_ = Layout::NOT_IN_TOC;
465                 max_toclevel_ = Layout::NOT_IN_TOC;
466                 const_iterator cit = begin();
467                 const_iterator the_end = end();
468                 for ( ; cit != the_end ; ++cit) {
469                         int const toclevel = (*cit)->toclevel;
470                         if (toclevel != Layout::NOT_IN_TOC) {
471                                 if (min_toclevel_ == Layout::NOT_IN_TOC)
472                                         min_toclevel_ = toclevel;
473                                 else
474                                         min_toclevel_ = min(min_toclevel_,
475                                                          toclevel);
476                                 max_toclevel_ = max(max_toclevel_,
477                                                          toclevel);
478                         }
479                 }
480                 LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
481                         << ", maximum is " << max_toclevel_);
482
483         }
484
485         return error;
486 }
487
488
489 void TextClass::readTitleType(Lexer & lexrc)
490 {
491         keyword_item titleTypeTags[] = {
492                 { "commandafter", TITLE_COMMAND_AFTER },
493                 { "environment", TITLE_ENVIRONMENT }
494         };
495
496         PushPopHelper pph(lexrc, titleTypeTags, TITLE_ENVIRONMENT);
497
498         int le = lexrc.lex();
499         switch (le) {
500         case Lexer::LEX_UNDEF:
501                 lexrc.printError("Unknown output type `$$Token'");
502                 return;
503         case TITLE_COMMAND_AFTER:
504         case TITLE_ENVIRONMENT:
505                 titletype_ = static_cast<TitleLatexType>(le);
506                 break;
507         default:
508                 lyxerr << "Unhandled value " << le
509                        << " in TextClass::readTitleType." << endl;
510
511                 break;
512         }
513 }
514
515
516 void TextClass::readOutputType(Lexer & lexrc)
517 {
518         keyword_item outputTypeTags[] = {
519                 { "docbook", DOCBOOK },
520                 { "latex", LATEX },
521                 { "literate", LITERATE }
522         };
523
524         PushPopHelper pph(lexrc, outputTypeTags, LITERATE);
525
526         int le = lexrc.lex();
527         switch (le) {
528         case Lexer::LEX_UNDEF:
529                 lexrc.printError("Unknown output type `$$Token'");
530                 return;
531         case LATEX:
532         case DOCBOOK:
533         case LITERATE:
534                 outputType_ = static_cast<OutputType>(le);
535                 break;
536         default:
537                 lyxerr << "Unhandled value " << le
538                        << " in TextClass::readOutputType." << endl;
539
540                 break;
541         }
542 }
543
544
545 enum ClassOptionsTags {
546         CO_FONTSIZE = 1,
547         CO_PAGESTYLE,
548         CO_OTHER,
549         CO_HEADER,
550         CO_END
551 };
552
553
554 void TextClass::readClassOptions(Lexer & lexrc)
555 {
556         keyword_item classOptionsTags[] = {
557                 {"end", CO_END },
558                 {"fontsize", CO_FONTSIZE },
559                 {"header", CO_HEADER },
560                 {"other", CO_OTHER },
561                 {"pagestyle", CO_PAGESTYLE }
562         };
563
564         lexrc.pushTable(classOptionsTags, CO_END);
565         bool getout = false;
566         while (!getout && lexrc.isOK()) {
567                 int le = lexrc.lex();
568                 switch (le) {
569                 case Lexer::LEX_UNDEF:
570                         lexrc.printError("Unknown ClassOption tag `$$Token'");
571                         continue;
572                 default: break;
573                 }
574                 switch (static_cast<ClassOptionsTags>(le)) {
575                 case CO_FONTSIZE:
576                         lexrc.next();
577                         opt_fontsize_ = rtrim(lexrc.getString());
578                         break;
579                 case CO_PAGESTYLE:
580                         lexrc.next();
581                         opt_pagestyle_ = rtrim(lexrc.getString());
582                         break;
583                 case CO_OTHER:
584                         lexrc.next();
585                         options_ = lexrc.getString();
586                         break;
587                 case CO_HEADER:
588                         lexrc.next();
589                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
590                         break;
591                 case CO_END:
592                         getout = true;
593                         break;
594                 }
595         }
596         lexrc.popTable();
597 }
598
599
600 enum InsetLayoutTags {
601         IL_FONT = 1,
602         IL_BGCOLOR,
603         IL_DECORATION,
604         IL_FREESPACING,
605         IL_FORCELTR,
606         IL_LABELFONT,
607         IL_LABELSTRING,
608         IL_LATEXNAME,
609         IL_LATEXPARAM,
610         IL_LATEXTYPE,
611         IL_LYXTYPE,
612         IL_KEEPEMPTY,
613         IL_MULTIPAR,
614         IL_NEEDPROTECT,
615         IL_PASSTHRU,
616         IL_PREAMBLE,
617         IL_REQUIRES,
618         IL_END
619 };
620
621
622 void TextClass::readInsetLayout(Lexer & lexrc, docstring const & name)
623 {
624         keyword_item elementTags[] = {
625                 { "bgcolor", IL_BGCOLOR },
626                 { "decoration", IL_DECORATION },
627                 { "end", IL_END },
628                 { "font", IL_FONT },
629                 { "forceltr", IL_FORCELTR },
630                 { "freespacing", IL_FREESPACING },
631                 { "keepempty", IL_KEEPEMPTY },
632                 { "labelfont", IL_LABELFONT },
633                 { "labelstring", IL_LABELSTRING },
634                 { "latexname", IL_LATEXNAME },
635                 { "latexparam", IL_LATEXPARAM },
636                 { "latextype", IL_LATEXTYPE },
637                 { "lyxtype", IL_LYXTYPE },
638                 { "multipar", IL_MULTIPAR },
639                 { "needprotect", IL_NEEDPROTECT },
640                 { "passthru", IL_PASSTHRU },
641                 { "preamble", IL_PREAMBLE },
642                 { "requires", IL_REQUIRES }
643         };
644
645         lexrc.pushTable(elementTags, IL_END);
646
647         string lyxtype;
648         docstring labelstring;
649         string latextype;
650         string decoration;
651         string latexname;
652         string latexparam;
653         FontInfo font = inherit_font;
654         FontInfo labelfont = inherit_font;
655         ColorCode bgcolor(Color_background);
656         string preamble;
657         set<string> requires;
658         bool multipar = false;
659         bool passthru = false;
660         bool needprotect = false;
661         bool keepempty = false;
662         bool freespacing = false;
663         bool forceltr = false;
664
665         bool getout = false;
666         while (!getout && lexrc.isOK()) {
667                 int le = lexrc.lex();
668                 switch (le) {
669                 case Lexer::LEX_UNDEF:
670                         lexrc.printError("Unknown InsetLayout tag `$$Token'");
671                         continue;
672                 default: break;
673                 }
674                 switch (static_cast<InsetLayoutTags>(le)) {
675                 case IL_LYXTYPE:
676                         lexrc.next();
677                         lyxtype = lexrc.getString();
678                         break;
679                 case IL_LATEXTYPE:
680                         lexrc.next();
681                         latextype = lexrc.getString();
682                         break;
683                 case IL_LABELSTRING:
684                         lexrc.next();
685                         labelstring = lexrc.getDocString();
686                         break;
687                 case IL_DECORATION:
688                         lexrc.next();
689                         decoration = lexrc.getString();
690                         break;
691                 case IL_LATEXNAME:
692                         lexrc.next();
693                         latexname = lexrc.getString();
694                         break;
695                 case IL_LATEXPARAM:
696                         lexrc.next();
697                         latexparam = subst(lexrc.getString(), "&quot;", "\"");
698                         break;
699                 case IL_LABELFONT:
700                         labelfont = lyxRead(lexrc, inherit_font);
701                         break;
702                 case IL_FORCELTR:
703                         lexrc.next();
704                         forceltr = lexrc.getBool();
705                         break;
706                 case IL_MULTIPAR:
707                         lexrc.next();
708                         multipar = lexrc.getBool();
709                         break;
710                 case IL_PASSTHRU:
711                         lexrc.next();
712                         passthru = lexrc.getBool();
713                         break;
714                 case IL_KEEPEMPTY:
715                         lexrc.next();
716                         keepempty = lexrc.getBool();
717                         break;
718                 case IL_FREESPACING:
719                         lexrc.next();
720                         freespacing = lexrc.getBool();
721                         break;
722                 case IL_NEEDPROTECT:
723                         lexrc.next();
724                         needprotect = lexrc.getBool();
725                         break;
726                 case IL_FONT:
727                         font = lyxRead(lexrc, inherit_font);
728                         // So: define font before labelfont
729                         labelfont = font;
730                         break;
731                 case IL_BGCOLOR: {
732                         lexrc.next();
733                         string const token = lexrc.getString();
734                         bgcolor = lcolor.getFromLyXName(token);
735                         break;
736                 }
737                 case IL_PREAMBLE:
738                         preamble = lexrc.getLongString("EndPreamble");
739                         break;
740                 case IL_REQUIRES: {
741                         lexrc.eatLine();
742                         vector<string> const req 
743                                 = getVectorFromString(lexrc.getString());
744                         requires.insert(req.begin(), req.end());
745                         break;
746                 }
747                 case IL_END:
748                         getout = true;
749                         break;
750                 }
751         }
752
753         // Here add element to list if getout == true
754         if (getout) {
755                 InsetLayout il;
756                 il.name = to_ascii(name);
757                 il.lyxtype = lyxtype;
758                 il.labelstring = labelstring;
759                 il.decoration = decoration;
760                 il.latextype = latextype;
761                 il.latexname = latexname;
762                 il.latexparam = latexparam;
763                 il.multipar = multipar;
764                 il.passthru = passthru;
765                 il.needprotect = needprotect;
766                 il.freespacing = freespacing;
767                 il.forceltr = forceltr;
768                 il.keepempty = keepempty;
769                 il.font = font;
770                 // The label font is generally used as-is without
771                 // any realization against a given context.
772                 labelfont.realize(sane_font);
773                 il.labelfont = labelfont;
774                 il.bgcolor = bgcolor;
775                 il.preamble = preamble;
776                 il.requires = requires;
777                 insetlayoutlist_[name] = il;
778         }
779
780         lexrc.popTable();
781 }
782
783
784
785 enum FloatTags {
786         FT_TYPE = 1,
787         FT_NAME,
788         FT_PLACEMENT,
789         FT_EXT,
790         FT_WITHIN,
791         FT_STYLE,
792         FT_LISTNAME,
793         FT_BUILTIN,
794         FT_END
795 };
796
797
798 void TextClass::readFloat(Lexer & lexrc)
799 {
800         keyword_item floatTags[] = {
801                 { "end", FT_END },
802                 { "extension", FT_EXT },
803                 { "guiname", FT_NAME },
804                 { "latexbuiltin", FT_BUILTIN },
805                 { "listname", FT_LISTNAME },
806                 { "numberwithin", FT_WITHIN },
807                 { "placement", FT_PLACEMENT },
808                 { "style", FT_STYLE },
809                 { "type", FT_TYPE }
810         };
811
812         lexrc.pushTable(floatTags, FT_END);
813
814         string type;
815         string placement;
816         string ext;
817         string within;
818         string style;
819         string name;
820         string listName;
821         bool builtin = false;
822
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 float tag `$$Token'");
829                         continue;
830                 default: break;
831                 }
832                 switch (static_cast<FloatTags>(le)) {
833                 case FT_TYPE:
834                         lexrc.next();
835                         type = lexrc.getString();
836                         if (floatlist_->typeExist(type)) {
837                                 Floating const & fl = floatlist_->getType(type);
838                                 placement = fl.placement();
839                                 ext = fl.ext();
840                                 within = fl.within();
841                                 style = fl.style();
842                                 name = fl.name();
843                                 listName = fl.listName();
844                                 builtin = fl.builtin();
845                         } 
846                         break;
847                 case FT_NAME:
848                         lexrc.next();
849                         name = lexrc.getString();
850                         break;
851                 case FT_PLACEMENT:
852                         lexrc.next();
853                         placement = lexrc.getString();
854                         break;
855                 case FT_EXT:
856                         lexrc.next();
857                         ext = lexrc.getString();
858                         break;
859                 case FT_WITHIN:
860                         lexrc.next();
861                         within = lexrc.getString();
862                         if (within == "none")
863                                 within.erase();
864                         break;
865                 case FT_STYLE:
866                         lexrc.next();
867                         style = lexrc.getString();
868                         break;
869                 case FT_LISTNAME:
870                         lexrc.next();
871                         listName = lexrc.getString();
872                         break;
873                 case FT_BUILTIN:
874                         lexrc.next();
875                         builtin = lexrc.getBool();
876                         break;
877                 case FT_END:
878                         getout = true;
879                         break;
880                 }
881         }
882
883         // Here if have a full float if getout == true
884         if (getout) {
885                 Floating fl(type, placement, ext, within,
886                             style, name, listName, builtin);
887                 floatlist_->newFloat(fl);
888                 // each float has its own counter
889                 counters_->newCounter(from_ascii(type), from_ascii(within), 
890                                       docstring(), docstring());
891         }
892
893         lexrc.popTable();
894 }
895
896
897 enum CounterTags {
898         CT_NAME = 1,
899         CT_WITHIN,
900         CT_LABELSTRING,
901         CT_LABELSTRING_APPENDIX,
902         CT_END
903 };
904
905
906 void TextClass::readCounter(Lexer & lexrc)
907 {
908         keyword_item counterTags[] = {
909                 { "end", CT_END },
910                 { "labelstring", CT_LABELSTRING },
911                 { "labelstringappendix", CT_LABELSTRING_APPENDIX },
912                 { "name", CT_NAME },
913                 { "within", CT_WITHIN }
914         };
915
916         lexrc.pushTable(counterTags, CT_END);
917
918         docstring name;
919         docstring within;
920         docstring labelstring;
921         docstring labelstring_appendix;
922
923         bool getout = false;
924         while (!getout && lexrc.isOK()) {
925                 int le = lexrc.lex();
926                 switch (le) {
927                 case Lexer::LEX_UNDEF:
928                         lexrc.printError("Unknown counter tag `$$Token'");
929                         continue;
930                 default: break;
931                 }
932                 switch (static_cast<CounterTags>(le)) {
933                 case CT_NAME:
934                         lexrc.next();
935                         name = lexrc.getDocString();
936                         if (counters_->hasCounter(name))
937                                 LYXERR(Debug::TCLASS, "Reading existing counter " << to_utf8(name));
938                         else
939                                 LYXERR(Debug::TCLASS, "Reading new counter " << to_utf8(name));
940                         break;
941                 case CT_WITHIN:
942                         lexrc.next();
943                         within = lexrc.getDocString();
944                         if (within == "none")
945                                 within.erase();
946                         break;
947                 case CT_LABELSTRING:
948                         lexrc.next();
949                         labelstring = lexrc.getDocString();
950                         labelstring_appendix = labelstring;
951                         break;
952                 case CT_LABELSTRING_APPENDIX:
953                         lexrc.next();
954                         labelstring_appendix = lexrc.getDocString();
955                         break;
956                 case CT_END:
957                         getout = true;
958                         break;
959                 }
960         }
961
962         // Here if have a full counter if getout == true
963         if (getout)
964                 counters_->newCounter(name, within, 
965                                       labelstring, labelstring_appendix);
966
967         lexrc.popTable();
968 }
969
970
971 FontInfo const & TextClass::defaultfont() const
972 {
973         return defaultfont_;
974 }
975
976
977 docstring const & TextClass::leftmargin() const
978 {
979         return leftmargin_;
980 }
981
982
983 docstring const & TextClass::rightmargin() const
984 {
985         return rightmargin_;
986 }
987
988
989 bool TextClass::hasLayout(docstring const & n) const
990 {
991         docstring const name = n.empty() ? defaultLayoutName() : n;
992
993         return find_if(layoutlist_.begin(), layoutlist_.end(),
994                        LayoutNamesEqual(name))
995                 != layoutlist_.end();
996 }
997
998
999
1000 LayoutPtr const & TextClass::operator[](docstring const & name) const
1001 {
1002         BOOST_ASSERT(!name.empty());
1003
1004         LayoutList::const_iterator cit =
1005                 find_if(layoutlist_.begin(),
1006                         layoutlist_.end(),
1007                         LayoutNamesEqual(name));
1008
1009         if (cit == layoutlist_.end()) {
1010                 lyxerr << "We failed to find the layout '" << to_utf8(name)
1011                        << "' in the layout list. You MUST investigate!"
1012                        << endl;
1013                 for (LayoutList::const_iterator it = layoutlist_.begin();
1014                          it != layoutlist_.end(); ++it)
1015                         lyxerr  << " " << to_utf8(it->get()->name()) << endl;
1016
1017                 // we require the name to exist
1018                 BOOST_ASSERT(false);
1019         }
1020
1021         return *cit;
1022 }
1023
1024
1025 bool TextClass::deleteLayout(docstring const & name)
1026 {
1027         if (name == defaultLayoutName())
1028                 return false;
1029
1030         LayoutList::iterator it =
1031                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1032                           LayoutNamesEqual(name));
1033
1034         LayoutList::iterator end = layoutlist_.end();
1035         bool const ret = (it != end);
1036         layoutlist_.erase(it, end);
1037         return ret;
1038 }
1039
1040
1041 // Load textclass info if not loaded yet
1042 bool TextClass::load(string const & path) const
1043 {
1044         if (loaded_)
1045                 return true;
1046
1047         // Read style-file, provided path is searched before the system ones
1048         FileName layout_file;
1049         if (!path.empty())
1050                 layout_file = FileName(addName(path, name_ + ".layout"));
1051         if (layout_file.empty() || !layout_file.exists())
1052                 layout_file = libFileSearch("layouts", name_, "layout");
1053         loaded_ = const_cast<TextClass*>(this)->read(layout_file) == 0;
1054
1055         if (!loaded_) {
1056                 lyxerr << "Error reading `"
1057                        << to_utf8(makeDisplayPath(layout_file.absFilename()))
1058                        << "'\n(Check `" << name_
1059                        << "')\nCheck your installation and "
1060                         "try Options/Reconfigure..." << endl;
1061         }
1062
1063         return loaded_;
1064 }
1065
1066
1067 FloatList & TextClass::floats()
1068 {
1069         return *floatlist_.get();
1070 }
1071
1072
1073 FloatList const & TextClass::floats() const
1074 {
1075         return *floatlist_.get();
1076 }
1077
1078
1079 Counters & TextClass::counters() const
1080 {
1081         return *counters_.get();
1082 }
1083
1084
1085 // Return the layout object of an inset given by name. If the name
1086 // is not found as such, the part after the ':' is stripped off, and
1087 // searched again. In this way, an error fallback can be provided:
1088 // An erroneous 'CharStyle:badname' (e.g., after a documentclass switch)
1089 // will invoke the layout object defined by name = 'CharStyle'.
1090 // If that doesn't work either, an empty object returns (shouldn't
1091 // happen).  -- Idea JMarc, comment MV
1092 InsetLayout const & TextClass::insetlayout(docstring const & name) const 
1093 {
1094         docstring n = name;
1095         while (!n.empty()) {
1096                 if (insetlayoutlist_.count(n) > 0)
1097                         return insetlayoutlist_[n];
1098                 docstring::size_type i = n.find(':');
1099                 if (i == string::npos)
1100                         break;
1101                 n = n.substr(0,i);
1102         }
1103         static InsetLayout empty;
1104         empty.labelstring = from_utf8("UNDEFINED");
1105         empty.labelfont = sane_font;
1106         empty.labelfont.setColor(Color_error);
1107         empty.bgcolor = Color_error;
1108         return empty;
1109 }
1110
1111
1112 docstring const & TextClass::defaultLayoutName() const
1113 {
1114         // This really should come from the actual layout... (Lgb)
1115         return defaultlayout_;
1116 }
1117
1118
1119 LayoutPtr const & TextClass::defaultLayout() const
1120 {
1121         return operator[](defaultLayoutName());
1122 }
1123
1124
1125 string const & TextClass::name() const
1126 {
1127         return name_;
1128 }
1129
1130
1131 string const & TextClass::latexname() const
1132 {
1133         const_cast<TextClass*>(this)->load();
1134         return latexname_;
1135 }
1136
1137
1138 string const & TextClass::description() const
1139 {
1140         return description_;
1141 }
1142
1143
1144 string const & TextClass::opt_fontsize() const
1145 {
1146         return opt_fontsize_;
1147 }
1148
1149
1150 string const & TextClass::opt_pagestyle() const
1151 {
1152         return opt_pagestyle_;
1153 }
1154
1155
1156 string const & TextClass::options() const
1157 {
1158         return options_;
1159 }
1160
1161
1162 string const & TextClass::class_header() const
1163 {
1164         return class_header_;
1165 }
1166
1167
1168 string const & TextClass::pagestyle() const
1169 {
1170         return pagestyle_;
1171 }
1172
1173
1174 docstring const & TextClass::preamble() const
1175 {
1176         return preamble_;
1177 }
1178
1179
1180 PageSides TextClass::sides() const
1181 {
1182         return sides_;
1183 }
1184
1185
1186 int TextClass::secnumdepth() const
1187 {
1188         return secnumdepth_;
1189 }
1190
1191
1192 int TextClass::tocdepth() const
1193 {
1194         return tocdepth_;
1195 }
1196
1197
1198 OutputType TextClass::outputType() const
1199 {
1200         return outputType_;
1201 }
1202
1203
1204 bool TextClass::provides(string const & p) const
1205 {
1206         return provides_.find(p) != provides_.end();
1207 }
1208
1209
1210 unsigned int TextClass::columns() const
1211 {
1212         return columns_;
1213 }
1214
1215
1216 TitleLatexType TextClass::titletype() const
1217 {
1218         return titletype_;
1219 }
1220
1221
1222 string const & TextClass::titlename() const
1223 {
1224         return titlename_;
1225 }
1226
1227
1228 int TextClass::size() const
1229 {
1230         return layoutlist_.size();
1231 }
1232
1233
1234 int TextClass::min_toclevel() const
1235 {
1236         return min_toclevel_;
1237 }
1238
1239
1240 int TextClass::max_toclevel() const
1241 {
1242         return max_toclevel_;
1243 }
1244
1245
1246 bool TextClass::hasTocLevels() const
1247 {
1248         return min_toclevel_ != Layout::NOT_IN_TOC;
1249 }
1250
1251
1252 ostream & operator<<(ostream & os, PageSides p)
1253 {
1254         switch (p) {
1255         case OneSide:
1256                 os << '1';
1257                 break;
1258         case TwoSides:
1259                 os << '2';
1260                 break;
1261         }
1262         return os;
1263 }
1264
1265
1266 } // namespace lyx