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