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