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