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