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