]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
BufferView.cpp: typo
[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                         if (options_.empty())
841                                 options_ = lexrc.getString();
842                         else
843                                 options_ += ',' + lexrc.getString();
844                         break;
845                 case CO_HEADER:
846                         lexrc.next();
847                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
848                         break;
849                 case CO_END:
850                         getout = true;
851                         break;
852                 }
853         }
854         lexrc.popTable();
855 }
856
857
858 void TextClass::readCiteFormat(Lexer & lexrc)
859 {
860         string etype;
861         string definition;
862         while (lexrc.isOK()) {
863                 lexrc.next();
864                 etype = lexrc.getString();
865                 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
866                         break;
867                 lexrc.eatLine();
868                 definition = lexrc.getString();
869                 char initchar = etype[0];
870                 if (initchar == '#')
871                         continue;
872                 if (initchar == '!' || initchar == '_')
873                         cite_macros_[etype] = definition;
874                 else
875                         cite_formats_[etype] = definition;
876         }
877 }
878
879
880 void TextClass::readFloat(Lexer & lexrc)
881 {
882         enum {
883                 FT_TYPE = 1,
884                 FT_NAME,
885                 FT_PLACEMENT,
886                 FT_EXT,
887                 FT_WITHIN,
888                 FT_STYLE,
889                 FT_LISTNAME,
890                 FT_NEEDSFLOAT,
891                 FT_HTMLSTYLE,
892                 FT_HTMLATTR,
893                 FT_HTMLTAG,
894                 FT_LISTCOMMAND,
895                 FT_REFPREFIX,
896                 FT_END
897         };
898
899         LexerKeyword floatTags[] = {
900                 { "end", FT_END },
901                 { "extension", FT_EXT },
902                 { "guiname", FT_NAME },
903                 { "htmlattr", FT_HTMLATTR },
904                 { "htmlstyle", FT_HTMLSTYLE },
905                 { "htmltag", FT_HTMLTAG },
906                 { "listcommand", FT_LISTCOMMAND },
907                 { "listname", FT_LISTNAME },
908                 { "needsfloatpkg", FT_NEEDSFLOAT },
909                 { "numberwithin", FT_WITHIN },
910                 { "placement", FT_PLACEMENT },
911                 { "refprefix", FT_REFPREFIX },
912                 { "style", FT_STYLE },
913                 { "type", FT_TYPE }
914         };
915
916         lexrc.pushTable(floatTags);
917
918         string ext;
919         string htmlattr;
920         string htmlstyle;
921         string htmltag;
922         string listname;
923         string listcommand;
924         string name;
925         string placement;
926         string refprefix;
927         string style;
928         string type;
929         string within;
930         bool needsfloat = true;
931
932         bool getout = false;
933         while (!getout && lexrc.isOK()) {
934                 int le = lexrc.lex();
935                 switch (le) {
936                 case Lexer::LEX_UNDEF:
937                         lexrc.printError("Unknown float tag `$$Token'");
938                         continue;
939                 default: break;
940                 }
941                 switch (le) {
942                 case FT_TYPE:
943                         lexrc.next();
944                         type = lexrc.getString();
945                         if (floatlist_.typeExist(type)) {
946                                 Floating const & fl = floatlist_.getType(type);
947                                 placement = fl.placement();
948                                 ext = fl.ext();
949                                 within = fl.within();
950                                 style = fl.style();
951                                 name = fl.name();
952                                 listname = fl.listName();
953                                 needsfloat = fl.needsFloatPkg();
954                                 listcommand = fl.listCommand();
955                                 refprefix = fl.refPrefix();
956                         } 
957                         break;
958                 case FT_NAME:
959                         lexrc.next();
960                         name = lexrc.getString();
961                         break;
962                 case FT_PLACEMENT:
963                         lexrc.next();
964                         placement = lexrc.getString();
965                         break;
966                 case FT_EXT:
967                         lexrc.next();
968                         ext = lexrc.getString();
969                         break;
970                 case FT_WITHIN:
971                         lexrc.next();
972                         within = lexrc.getString();
973                         if (within == "none")
974                                 within.erase();
975                         break;
976                 case FT_STYLE:
977                         lexrc.next();
978                         style = lexrc.getString();
979                         break;
980                 case FT_LISTCOMMAND:
981                         lexrc.next();
982                         listcommand = lexrc.getString();
983                         break;
984                 case FT_REFPREFIX:
985                         lexrc.next();
986                         refprefix = lexrc.getString();
987                         break;
988                 case FT_LISTNAME:
989                         lexrc.next();
990                         listname = lexrc.getString();
991                         break;
992                 case FT_NEEDSFLOAT:
993                         lexrc.next();
994                         needsfloat = lexrc.getBool();
995                         break;
996                 case FT_HTMLATTR:
997                         lexrc.next();
998                         htmlattr = lexrc.getString();
999                         break;
1000                 case FT_HTMLSTYLE:
1001                         lexrc.next();
1002                         htmlstyle = lexrc.getLongString("EndHTMLStyle");
1003                         break;
1004                 case FT_HTMLTAG:
1005                         lexrc.next();
1006                         htmltag = lexrc.getString();
1007                         break;
1008                 case FT_END:
1009                         getout = true;
1010                         break;
1011                 }
1012         }
1013
1014         // Here we have a full float if getout == true
1015         if (getout) {
1016                 if (!needsfloat && listcommand.empty())
1017                         LYXERR0("The layout does not provide a list command " <<
1018                                 "for the builtin float `" << type << "'. LyX will " <<
1019                                 "not be able to produce a float list.");
1020                 Floating fl(type, placement, ext, within, style, name, 
1021                                 listname, listcommand, refprefix, 
1022                                 htmltag, htmlattr, htmlstyle, needsfloat);
1023                 floatlist_.newFloat(fl);
1024                 // each float has its own counter
1025                 counters_.newCounter(from_ascii(type), from_ascii(within),
1026                                       docstring(), docstring());
1027                 // also define sub-float counters
1028                 docstring const subtype = "sub-" + from_ascii(type);
1029                 counters_.newCounter(subtype, from_ascii(type),
1030                                       "\\alph{" + subtype + "}", docstring());
1031         }
1032
1033         lexrc.popTable();
1034 }
1035
1036
1037 string const & TextClass::prerequisites() const
1038
1039         if (contains(prerequisites_, ',')) {
1040                 vector<string> const pres = getVectorFromString(prerequisites_);
1041                 prerequisites_ = getStringFromVector(pres, "\n\t");
1042         }
1043         return prerequisites_; 
1044 }
1045
1046 bool TextClass::hasLayout(docstring const & n) const
1047 {
1048         docstring const name = n.empty() ? defaultLayoutName() : n;
1049
1050         return find_if(layoutlist_.begin(), layoutlist_.end(),
1051                        LayoutNamesEqual(name))
1052                 != layoutlist_.end();
1053 }
1054
1055
1056 bool TextClass::hasInsetLayout(docstring const & n) const
1057 {
1058         if (n.empty()) 
1059                 return false;
1060         InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1061         InsetLayouts::const_iterator en = insetlayoutlist_.end();
1062         for (; it != en; ++it)
1063                 if (n == it->first)
1064                         return true;
1065         return false;
1066 }
1067
1068
1069 Layout const & TextClass::operator[](docstring const & name) const
1070 {
1071         LASSERT(!name.empty(), /**/);
1072
1073         const_iterator it = 
1074                 find_if(begin(), end(), LayoutNamesEqual(name));
1075
1076         if (it == end()) {
1077                 lyxerr << "We failed to find the layout '" << to_utf8(name)
1078                        << "' in the layout list. You MUST investigate!"
1079                        << endl;
1080                 for (const_iterator cit = begin(); cit != end(); ++cit)
1081                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1082
1083                 // we require the name to exist
1084                 LASSERT(false, /**/);
1085         }
1086
1087         return *it;
1088 }
1089
1090
1091 Layout & TextClass::operator[](docstring const & name)
1092 {
1093         LASSERT(!name.empty(), /**/);
1094
1095         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1096
1097         if (it == end()) {
1098                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1099                        << "' in the layout list. You MUST investigate!");
1100                 for (const_iterator cit = begin(); cit != end(); ++cit)
1101                         LYXERR0(" " << to_utf8(cit->name()));
1102
1103                 // we require the name to exist
1104                 LASSERT(false, /**/);
1105         }
1106
1107         return *it;
1108 }
1109
1110
1111 bool TextClass::deleteLayout(docstring const & name)
1112 {
1113         if (name == defaultLayoutName() || name == plainLayoutName())
1114                 return false;
1115
1116         LayoutList::iterator it =
1117                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1118                           LayoutNamesEqual(name));
1119
1120         LayoutList::iterator end = layoutlist_.end();
1121         bool const ret = (it != end);
1122         layoutlist_.erase(it, end);
1123         return ret;
1124 }
1125
1126
1127 // Load textclass info if not loaded yet
1128 bool TextClass::load(string const & path) const
1129 {
1130         if (loaded_)
1131                 return true;
1132
1133         // Read style-file, provided path is searched before the system ones
1134         // If path is a file, it is loaded directly.
1135         FileName layout_file(path);
1136         if (!path.empty() && !layout_file.isReadableFile())
1137                 layout_file = FileName(addName(path, name_ + ".layout"));
1138         if (layout_file.empty() || !layout_file.exists())
1139                 layout_file = libFileSearch("layouts", name_, "layout");
1140         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1141
1142         if (!loaded_) {
1143                 lyxerr << "Error reading `"
1144                        << to_utf8(makeDisplayPath(layout_file.absFilename()))
1145                        << "'\n(Check `" << name_
1146                        << "')\nCheck your installation and "
1147                           "try Options/Reconfigure..." 
1148                        << endl;
1149         }
1150
1151         return loaded_;
1152 }
1153
1154
1155 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1156 {
1157         if (!hasLayout(n))
1158                 layoutlist_.push_back(createBasicLayout(n, true));
1159 }
1160
1161
1162 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const 
1163 {
1164         // FIXME The fix for the InsetLayout part of 4812 would be here:
1165         // Add the InsetLayout to the document class if it is not found.
1166         docstring n = name;
1167         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1168         while (!n.empty()) {
1169                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1170                 if (cit != cen && cit->first == n)
1171                         return cit->second;
1172                 size_t i = n.find(':');
1173                 if (i == string::npos)
1174                         break;
1175                 n = n.substr(0, i);
1176         }
1177         return plain_insetlayout_;
1178 }
1179
1180
1181 docstring const & TextClass::defaultLayoutName() const
1182 {
1183         // This really should come from the actual layout... (Lgb)
1184         return defaultlayout_;
1185 }
1186
1187
1188 Layout const & TextClass::defaultLayout() const
1189 {
1190         return operator[](defaultLayoutName());
1191 }
1192
1193
1194 bool TextClass::isDefaultLayout(Layout const & layout) const 
1195 {
1196         return layout.name() == defaultLayoutName();
1197 }
1198
1199
1200 bool TextClass::isPlainLayout(Layout const & layout) const 
1201 {
1202         return layout.name() == plainLayoutName();
1203 }
1204
1205
1206 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1207 {
1208         static Layout * defaultLayout = NULL;
1209
1210         if (defaultLayout) {
1211                 defaultLayout->setUnknown(unknown);
1212                 defaultLayout->setName(name);
1213                 return *defaultLayout;
1214         }
1215
1216         static char const * s = "Margin Static\n"
1217                         "LatexType Paragraph\n"
1218                         "LatexName dummy\n"
1219                         "Align Block\n"
1220                         "AlignPossible Left, Right, Center\n"
1221                         "LabelType No_Label\n"
1222                         "End";
1223         istringstream ss(s);
1224         Lexer lex(textClassTags);
1225         lex.setStream(ss);
1226         defaultLayout = new Layout;
1227         defaultLayout->setUnknown(unknown);
1228         defaultLayout->setName(name);
1229         if (!readStyle(lex, *defaultLayout)) {
1230                 // The only way this happens is because the hardcoded layout above
1231                 // is wrong.
1232                 LASSERT(false, /**/);
1233         };
1234         return *defaultLayout;
1235 }
1236
1237
1238 /////////////////////////////////////////////////////////////////////////
1239 //
1240 // DocumentClassBundle
1241 //
1242 /////////////////////////////////////////////////////////////////////////
1243
1244 DocumentClassBundle::~DocumentClassBundle()
1245 {
1246         for (size_t i = 0; i != documentClasses_.size(); ++i)
1247                 delete documentClasses_[i];
1248         documentClasses_.clear();
1249 }
1250
1251 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1252 {
1253         DocumentClass * dc = new DocumentClass(baseClass);
1254         documentClasses_.push_back(dc);
1255         return *documentClasses_.back();
1256 }
1257
1258
1259 DocumentClassBundle & DocumentClassBundle::get()
1260 {
1261         static DocumentClassBundle singleton; 
1262         return singleton; 
1263 }
1264
1265
1266 DocumentClass & DocumentClassBundle::makeDocumentClass(
1267                 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1268 {
1269         DocumentClass & doc_class = newClass(baseClass);
1270         LayoutModuleList::const_iterator it = modlist.begin();
1271         LayoutModuleList::const_iterator en = modlist.end();
1272         for (; it != en; it++) {
1273                 string const modName = *it;
1274                 LyXModule * lm = theModuleList[modName];
1275                 if (!lm) {
1276                         docstring const msg =
1277                                                 bformat(_("The module %1$s has been requested by\n"
1278                                                 "this document but has not been found in the list of\n"
1279                                                 "available modules. If you recently installed it, you\n"
1280                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1281                         ExceptionMessage(WarningException,_("Module not available"),
1282                                         msg + _("Some layouts may not be available."));
1283                         continue;
1284                 }
1285                 if (!lm->isAvailable()) {
1286                         docstring const msg =
1287                                                 bformat(_("The module %1$s requires a package that is\n"
1288                                                 "not available in your LaTeX installation. LaTeX output\n"
1289                                                 "may not be possible.\n"), from_utf8(modName));
1290                         ExceptionMessage(WarningException, _("Package not available"), msg);
1291                 }
1292                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1293                 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1294                         docstring const msg =
1295                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1296                         throw ExceptionMessage(WarningException, _("Read Error"), msg);
1297                 }
1298         }
1299         return doc_class;
1300 }
1301
1302
1303 /////////////////////////////////////////////////////////////////////////
1304 //
1305 // DocumentClass
1306 //
1307 /////////////////////////////////////////////////////////////////////////
1308
1309 DocumentClass::DocumentClass(LayoutFile const & tc)
1310         : TextClass(tc)
1311 {}
1312
1313
1314 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1315 {
1316         LayoutList::const_iterator it  = layoutlist_.begin();
1317         LayoutList::const_iterator end = layoutlist_.end();
1318         for (; it != end; ++it)
1319                 if (it->latexname() == lay)
1320                         return true;
1321         return false;
1322 }
1323
1324
1325 bool DocumentClass::provides(string const & p) const
1326 {
1327         return provides_.find(p) != provides_.end();
1328 }
1329
1330
1331 bool DocumentClass::hasTocLevels() const
1332 {
1333         return min_toclevel_ != Layout::NOT_IN_TOC;
1334 }
1335
1336
1337 Layout const & DocumentClass::htmlTOCLayout() const
1338 {
1339         if (html_toc_section_.empty()) {
1340                 // we're going to look for the layout with the minimum toclevel
1341                 TextClass::LayoutList::const_iterator lit = begin();
1342                 TextClass::LayoutList::const_iterator const len = end();
1343                 int minlevel = 1000;
1344                 Layout const * lay = NULL;
1345                 for (; lit != len; ++lit) {
1346                         int const level = lit->toclevel;
1347                         // we don't want Part
1348                         if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1349                                 continue;
1350                         lay = &*lit;
1351                         minlevel = level;
1352                 }
1353                 if (lay)
1354                         html_toc_section_ = lay->name();
1355                 else
1356                         // hmm. that is very odd, so we'll do our best
1357                         html_toc_section_ = defaultLayoutName();
1358         }
1359         return operator[](html_toc_section_);
1360 }
1361
1362
1363 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1364 {
1365         static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1366         
1367         map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1368         if (it != cite_formats_.end())
1369                 return it->second;
1370         return default_format;
1371 }
1372
1373
1374 string const & DocumentClass::getCiteMacro(string const & macro) const
1375 {
1376         static string empty;
1377         map<string, string>::const_iterator it = cite_macros_.find(macro);
1378         if (it != cite_macros_.end())
1379                 return it->second;
1380         return empty;
1381 }
1382
1383
1384 /////////////////////////////////////////////////////////////////////////
1385 //
1386 // PageSides
1387 //
1388 /////////////////////////////////////////////////////////////////////////
1389
1390 ostream & operator<<(ostream & os, PageSides p)
1391 {
1392         switch (p) {
1393         case OneSide:
1394                 os << '1';
1395                 break;
1396         case TwoSides:
1397                 os << '2';
1398                 break;
1399         }
1400         return os;
1401 }
1402
1403
1404 } // namespace lyx