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