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