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