]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Just some renaming, preparatory to disentangling two jobs the current
[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 = 32;
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 usesfloat = 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                                 usesfloat = fl.usesFloatPkg();
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                         usesfloat = 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 (!usesfloat && listcommand.empty()) {
1044                         // if this float uses the same auxfile as an existing one,
1045                         // there is no need for it to provide a list command.
1046                         FloatList::const_iterator it = floatlist_.begin();
1047                         FloatList::const_iterator en = floatlist_.end();
1048                         bool found_ext = false;
1049                         for (; it != en; ++it) {
1050                                 if (it->second.ext() == ext) {
1051                                         found_ext = true;
1052                                         break;
1053                                 }
1054                         }
1055                         if (!found_ext)
1056                                 LYXERR0("The layout does not provide a list command " <<
1057                                   "for the float `" << type << "'. LyX will " <<
1058                                   "not be able to produce a float list.");
1059                 }
1060                 Floating fl(type, placement, ext, within, style, name, 
1061                                 listname, listcommand, refprefix, 
1062                                 htmltag, htmlattr, htmlstyle, usesfloat);
1063                 floatlist_.newFloat(fl);
1064                 // each float has its own counter
1065                 counters_.newCounter(from_ascii(type), from_ascii(within),
1066                                       docstring(), docstring());
1067                 // also define sub-float counters
1068                 docstring const subtype = "sub-" + from_ascii(type);
1069                 counters_.newCounter(subtype, from_ascii(type),
1070                                       "\\alph{" + subtype + "}", docstring());
1071         }
1072         return getout;
1073 }
1074
1075
1076 string const & TextClass::prerequisites() const
1077
1078         if (contains(prerequisites_, ',')) {
1079                 vector<string> const pres = getVectorFromString(prerequisites_);
1080                 prerequisites_ = getStringFromVector(pres, "\n\t");
1081         }
1082         return prerequisites_; 
1083 }
1084
1085 bool TextClass::hasLayout(docstring const & n) const
1086 {
1087         docstring const name = n.empty() ? defaultLayoutName() : n;
1088
1089         return find_if(layoutlist_.begin(), layoutlist_.end(),
1090                        LayoutNamesEqual(name))
1091                 != layoutlist_.end();
1092 }
1093
1094
1095 bool TextClass::hasInsetLayout(docstring const & n) const
1096 {
1097         if (n.empty()) 
1098                 return false;
1099         InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1100         InsetLayouts::const_iterator en = insetlayoutlist_.end();
1101         for (; it != en; ++it)
1102                 if (n == it->first)
1103                         return true;
1104         return false;
1105 }
1106
1107
1108 Layout const & TextClass::operator[](docstring const & name) const
1109 {
1110         LASSERT(!name.empty(), /**/);
1111
1112         const_iterator it = 
1113                 find_if(begin(), end(), LayoutNamesEqual(name));
1114
1115         if (it == end()) {
1116                 lyxerr << "We failed to find the layout '" << to_utf8(name)
1117                        << "' in the layout list. You MUST investigate!"
1118                        << endl;
1119                 for (const_iterator cit = begin(); cit != end(); ++cit)
1120                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1121
1122                 // we require the name to exist
1123                 LASSERT(false, /**/);
1124         }
1125
1126         return *it;
1127 }
1128
1129
1130 Layout & TextClass::operator[](docstring const & name)
1131 {
1132         LASSERT(!name.empty(), /**/);
1133
1134         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1135
1136         if (it == end()) {
1137                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1138                        << "' in the layout list. You MUST investigate!");
1139                 for (const_iterator cit = begin(); cit != end(); ++cit)
1140                         LYXERR0(" " << to_utf8(cit->name()));
1141
1142                 // we require the name to exist
1143                 LASSERT(false, /**/);
1144         }
1145
1146         return *it;
1147 }
1148
1149
1150 bool TextClass::deleteLayout(docstring const & name)
1151 {
1152         if (name == defaultLayoutName() || name == plainLayoutName())
1153                 return false;
1154
1155         LayoutList::iterator it =
1156                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1157                           LayoutNamesEqual(name));
1158
1159         LayoutList::iterator end = layoutlist_.end();
1160         bool const ret = (it != end);
1161         layoutlist_.erase(it, end);
1162         return ret;
1163 }
1164
1165
1166 // Load textclass info if not loaded yet
1167 bool TextClass::load(string const & path) const
1168 {
1169         if (loaded_)
1170                 return true;
1171
1172         // Read style-file, provided path is searched before the system ones
1173         // If path is a file, it is loaded directly.
1174         FileName layout_file(path);
1175         if (!path.empty() && !layout_file.isReadableFile())
1176                 layout_file = FileName(addName(path, name_ + ".layout"));
1177         if (layout_file.empty() || !layout_file.exists())
1178                 layout_file = libFileSearch("layouts", name_, "layout");
1179         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1180
1181         if (!loaded_) {
1182                 lyxerr << "Error reading `"
1183                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1184                        << "'\n(Check `" << name_
1185                        << "')\nCheck your installation and "
1186                           "try Options/Reconfigure..." 
1187                        << endl;
1188         }
1189
1190         return loaded_;
1191 }
1192
1193
1194 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1195 {
1196         if (!hasLayout(n))
1197                 layoutlist_.push_back(createBasicLayout(n, true));
1198 }
1199
1200
1201 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const 
1202 {
1203         // FIXME The fix for the InsetLayout part of 4812 would be here:
1204         // Add the InsetLayout to the document class if it is not found.
1205         docstring n = name;
1206         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1207         while (!n.empty()) {
1208                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1209                 if (cit != cen && cit->first == n)
1210                         return cit->second;
1211                 size_t i = n.find(':');
1212                 if (i == string::npos)
1213                         break;
1214                 n = n.substr(0, i);
1215         }
1216         return plain_insetlayout_;
1217 }
1218
1219
1220 docstring const & TextClass::defaultLayoutName() const
1221 {
1222         // This really should come from the actual layout... (Lgb)
1223         return defaultlayout_;
1224 }
1225
1226
1227 Layout const & TextClass::defaultLayout() const
1228 {
1229         return operator[](defaultLayoutName());
1230 }
1231
1232
1233 bool TextClass::isDefaultLayout(Layout const & layout) const 
1234 {
1235         return layout.name() == defaultLayoutName();
1236 }
1237
1238
1239 bool TextClass::isPlainLayout(Layout const & layout) const 
1240 {
1241         return layout.name() == plainLayoutName();
1242 }
1243
1244
1245 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1246 {
1247         static Layout * defaultLayout = NULL;
1248
1249         if (defaultLayout) {
1250                 defaultLayout->setUnknown(unknown);
1251                 defaultLayout->setName(name);
1252                 return *defaultLayout;
1253         }
1254
1255         static char const * s = "Margin Static\n"
1256                         "LatexType Paragraph\n"
1257                         "LatexName dummy\n"
1258                         "Align Block\n"
1259                         "AlignPossible Left, Right, Center\n"
1260                         "LabelType No_Label\n"
1261                         "End";
1262         istringstream ss(s);
1263         Lexer lex(textClassTags);
1264         lex.setStream(ss);
1265         defaultLayout = new Layout;
1266         defaultLayout->setUnknown(unknown);
1267         defaultLayout->setName(name);
1268         if (!readStyle(lex, *defaultLayout)) {
1269                 // The only way this happens is because the hardcoded layout above
1270                 // is wrong.
1271                 LASSERT(false, /**/);
1272         };
1273         return *defaultLayout;
1274 }
1275
1276
1277 /////////////////////////////////////////////////////////////////////////
1278 //
1279 // DocumentClassBundle
1280 //
1281 /////////////////////////////////////////////////////////////////////////
1282
1283 DocumentClassBundle::~DocumentClassBundle()
1284 {
1285         for (size_t i = 0; i != documentClasses_.size(); ++i)
1286                 delete documentClasses_[i];
1287         documentClasses_.clear();
1288 }
1289
1290 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1291 {
1292         DocumentClass * dc = new DocumentClass(baseClass);
1293         documentClasses_.push_back(dc);
1294         return *documentClasses_.back();
1295 }
1296
1297
1298 DocumentClassBundle & DocumentClassBundle::get()
1299 {
1300         static DocumentClassBundle singleton; 
1301         return singleton; 
1302 }
1303
1304
1305 DocumentClass & DocumentClassBundle::makeDocumentClass(
1306                 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1307 {
1308         DocumentClass & doc_class = newClass(baseClass);
1309         LayoutModuleList::const_iterator it = modlist.begin();
1310         LayoutModuleList::const_iterator en = modlist.end();
1311         for (; it != en; it++) {
1312                 string const modName = *it;
1313                 LyXModule * lm = theModuleList[modName];
1314                 if (!lm) {
1315                         docstring const msg =
1316                                                 bformat(_("The module %1$s has been requested by\n"
1317                                                 "this document but has not been found in the list of\n"
1318                                                 "available modules. If you recently installed it, you\n"
1319                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1320                         frontend::Alert::warning(_("Module not available"), msg);
1321                         continue;
1322                 }
1323                 if (!lm->isAvailable()) {
1324                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1325                         docstring const msg =
1326                                 bformat(_("The module %1$s requires a package that is not\n"
1327                                         "available in your LaTeX installation, or a converter that\n"
1328                                         "you have not installed. LaTeX output may not be possible.\n"
1329                                         "Missing prerequisites:\n"
1330                                                 "\t%2$s\n"
1331                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1332                                 from_utf8(modName), prereqs);
1333                         frontend::Alert::warning(_("Package not available"), msg, true);
1334                 }
1335                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1336                 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1337                         docstring const msg =
1338                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1339                         frontend::Alert::warning(_("Read Error"), msg);
1340                 }
1341         }
1342         return doc_class;
1343 }
1344
1345
1346 /////////////////////////////////////////////////////////////////////////
1347 //
1348 // DocumentClass
1349 //
1350 /////////////////////////////////////////////////////////////////////////
1351
1352 DocumentClass::DocumentClass(LayoutFile const & tc)
1353         : TextClass(tc)
1354 {}
1355
1356
1357 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1358 {
1359         LayoutList::const_iterator it  = layoutlist_.begin();
1360         LayoutList::const_iterator end = layoutlist_.end();
1361         for (; it != end; ++it)
1362                 if (it->latexname() == lay)
1363                         return true;
1364         return false;
1365 }
1366
1367
1368 bool DocumentClass::provides(string const & p) const
1369 {
1370         return provides_.find(p) != provides_.end();
1371 }
1372
1373
1374 bool DocumentClass::hasTocLevels() const
1375 {
1376         return min_toclevel_ != Layout::NOT_IN_TOC;
1377 }
1378
1379
1380 Layout const & DocumentClass::htmlTOCLayout() const
1381 {
1382         if (html_toc_section_.empty()) {
1383                 // we're going to look for the layout with the minimum toclevel
1384                 TextClass::LayoutList::const_iterator lit = begin();
1385                 TextClass::LayoutList::const_iterator const len = end();
1386                 int minlevel = 1000;
1387                 Layout const * lay = NULL;
1388                 for (; lit != len; ++lit) {
1389                         int const level = lit->toclevel;
1390                         // we don't want Part
1391                         if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1392                                 continue;
1393                         lay = &*lit;
1394                         minlevel = level;
1395                 }
1396                 if (lay)
1397                         html_toc_section_ = lay->name();
1398                 else
1399                         // hmm. that is very odd, so we'll do our best
1400                         html_toc_section_ = defaultLayoutName();
1401         }
1402         return operator[](html_toc_section_);
1403 }
1404
1405
1406 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1407 {
1408         static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1409         
1410         map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1411         if (it != cite_formats_.end())
1412                 return it->second;
1413         return default_format;
1414 }
1415
1416
1417 string const & DocumentClass::getCiteMacro(string const & macro) const
1418 {
1419         static string empty;
1420         map<string, string>::const_iterator it = cite_macros_.find(macro);
1421         if (it != cite_macros_.end())
1422                 return it->second;
1423         return empty;
1424 }
1425
1426
1427 /////////////////////////////////////////////////////////////////////////
1428 //
1429 // PageSides
1430 //
1431 /////////////////////////////////////////////////////////////////////////
1432
1433 ostream & operator<<(ostream & os, PageSides p)
1434 {
1435         switch (p) {
1436         case OneSide:
1437                 os << '1';
1438                 break;
1439         case TwoSides:
1440                 os << '2';
1441                 break;
1442         }
1443         return os;
1444 }
1445
1446
1447 } // namespace lyx