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