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