]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Fix computation of string width when using a QTextLayout
[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
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
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         // The first usable line should be
412         // Format LAYOUT_FORMAT
413         if (lexrc.lex() != TC_FORMAT || !lexrc.next()
414             || lexrc.getInteger() != LAYOUT_FORMAT)
415                 return FORMAT_MISMATCH;
416
417         // parsing
418         bool error = false;
419         while (lexrc.isOK() && !error) {
420                 int le = lexrc.lex();
421
422                 switch (le) {
423                 case Lexer::LEX_FEOF:
424                         continue;
425
426                 case Lexer::LEX_UNDEF:
427                         lexrc.printError("Unknown TextClass tag `$$Token'");
428                         error = true;
429                         continue;
430
431                 default:
432                         break;
433                 }
434
435                 // used below to track whether we are in an IfStyle or IfCounter tag.
436                 bool modifystyle  = false;
437                 bool providestyle = false;
438                 bool ifcounter    = false;
439
440                 switch (static_cast<TextClassTags>(le)) {
441
442                 case TC_FORMAT:
443                         lexrc.next();
444                         lexrc.printError("Duplicate Format directive");
445                         break;
446
447                 case TC_OUTPUTFORMAT:
448                         if (lexrc.next())
449                                 outputFormat_ = lexrc.getString();
450                         break;
451
452                 case TC_OUTPUTTYPE:
453                         readOutputType(lexrc);
454                         switch(outputType_) {
455                         case LATEX:
456                                 outputFormat_ = "latex";
457                                 break;
458                         case DOCBOOK:
459                                 outputFormat_ = "docbook";
460                                 break;
461                         case LITERATE:
462                                 outputFormat_ = "literate";
463                                 break;
464                         }
465                         break;
466
467                 case TC_INPUT: // Include file
468                         if (lexrc.next()) {
469                                 FileName tmp;
470                                 string const inc = lexrc.getString();
471                                 if (!path().empty() && (prefixIs(inc, "./") ||
472                                                         prefixIs(inc, "../")))
473                                         tmp = fileSearch(path(), inc, "layout");
474                                 else
475                                         tmp = libFileSearch("layouts", inc,
476                                                             "layout");
477
478                                 if (tmp.empty()) {
479                                         lexrc.printError("Could not find input file: " + inc);
480                                         error = true;
481                                 } else if (!read(tmp, MERGE)) {
482                                         lexrc.printError("Error reading input file: " + tmp.absFileName());
483                                         error = true;
484                                 }
485                         }
486                         break;
487
488                 case TC_DEFAULTSTYLE:
489                         if (lexrc.next()) {
490                                 docstring const name = from_utf8(subst(lexrc.getString(),
491                                                           '_', ' '));
492                                 defaultlayout_ = name;
493                         }
494                         break;
495
496                 case TC_MODIFYSTYLE:
497                         modifystyle = true;
498                 // fall through
499                 case TC_PROVIDESTYLE:
500                         // if modifystyle is true, then we got here by falling through
501                         // so we are not in an ProvideStyle block
502                         if (!modifystyle)
503                                 providestyle = true;
504                 // fall through
505                 case TC_STYLE: {
506                         if (!lexrc.next()) {
507                                 lexrc.printError("No name given for style: `$$Token'.");
508                                 error = true;
509                                 break;
510                         }
511                         docstring const name = from_utf8(subst(lexrc.getString(),
512                                                         '_', ' '));
513                         if (name.empty()) {
514                                 string s = "Could not read name for style: `$$Token' "
515                                         + lexrc.getString() + " is probably not valid UTF-8!";
516                                 lexrc.printError(s);
517                                 Layout lay;
518                                 // Since we couldn't read the name, we just scan the rest
519                                 // of the style and discard it.
520                                 error = !readStyle(lexrc, lay);
521                                 break;
522                         }
523
524                         bool const have_layout = hasLayout(name);
525
526                         // If the layout already exists, then we want to add it to
527                         // the existing layout, as long as we are not in an ProvideStyle
528                         // block.
529                         if (have_layout && !providestyle) {
530                                 Layout & lay = operator[](name);
531                                 error = !readStyle(lexrc, lay);
532                         }
533                         // If the layout does not exist, then we want to create a new
534                         // one, but not if we are in a ModifyStyle block.
535                         else if (!have_layout && !modifystyle) {
536                                 Layout layout;
537                                 layout.setName(name);
538                                 error = !readStyle(lexrc, layout);
539                                 if (!error)
540                                         layoutlist_.push_back(layout);
541
542                                 if (defaultlayout_.empty()) {
543                                         // We do not have a default layout yet, so we choose
544                                         // the first layout we encounter.
545                                         defaultlayout_ = name;
546                                 }
547                         }
548                         // There are two ways to get here:
549                         //  (i)  The layout exists but we are in an ProvideStyle block
550                         //  (ii) The layout doesn't exist, but we are in an ModifyStyle
551                         //       block.
552                         // Either way, we just scan the rest and discard it
553                         else {
554                                 Layout lay;
555                                 // signal to coverity that we do not care about the result
556                                 (void)readStyle(lexrc, lay);
557                         }
558                         break;
559                 }
560
561                 case TC_NOSTYLE:
562                         if (lexrc.next()) {
563                                 docstring const style = from_utf8(subst(lexrc.getString(),
564                                                      '_', ' '));
565                                 if (!deleteLayout(style))
566                                         lyxerr << "Cannot delete style `"
567                                                << to_utf8(style) << '\'' << endl;
568                         }
569                         break;
570
571                 case TC_NOINSETLAYOUT:
572                         if (lexrc.next()) {
573                                 docstring const style = from_utf8(subst(lexrc.getString(),
574                                                                  '_', ' '));
575                                 if (!deleteInsetLayout(style))
576                                         LYXERR0("Style `" << style << "' cannot be removed\n"
577                                                 "because it was not found!");
578                         }
579                         break;
580
581                 case TC_COLUMNS:
582                         if (lexrc.next())
583                                 columns_ = lexrc.getInteger();
584                         break;
585
586                 case TC_SIDES:
587                         if (lexrc.next()) {
588                                 switch (lexrc.getInteger()) {
589                                 case 1: sides_ = OneSide; break;
590                                 case 2: sides_ = TwoSides; break;
591                                 default:
592                                         lyxerr << "Impossible number of page"
593                                                 " sides, setting to one."
594                                                << endl;
595                                         sides_ = OneSide;
596                                         break;
597                                 }
598                         }
599                         break;
600
601                 case TC_PAGESTYLE:
602                         lexrc.next();
603                         pagestyle_ = rtrim(lexrc.getString());
604                         break;
605
606                 case TC_DEFAULTFONT:
607                         defaultfont_ = lyxRead(lexrc);
608                         if (!defaultfont_.resolved()) {
609                                 lexrc.printError("Warning: defaultfont should "
610                                                  "be fully instantiated!");
611                                 defaultfont_.realize(sane_font);
612                         }
613                         break;
614
615                 case TC_SECNUMDEPTH:
616                         lexrc.next();
617                         secnumdepth_ = lexrc.getInteger();
618                         break;
619
620                 case TC_TOCDEPTH:
621                         lexrc.next();
622                         tocdepth_ = lexrc.getInteger();
623                         break;
624
625                 // First step to support options
626                 case TC_CLASSOPTIONS:
627                         readClassOptions(lexrc);
628                         break;
629
630                 case TC_PREAMBLE:
631                         preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
632                         break;
633
634                 case TC_HTMLPREAMBLE:
635                         htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
636                         break;
637
638                 case TC_HTMLSTYLES:
639                         htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
640                         break;
641
642                 case TC_HTMLTOCSECTION:
643                         html_toc_section_ = from_utf8(trim(lexrc.getString()));
644                         break;
645
646                 case TC_ADDTOPREAMBLE:
647                         preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
648                         break;
649
650                 case TC_ADDTOHTMLPREAMBLE:
651                         htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
652                         break;
653
654                 case TC_ADDTOHTMLSTYLES:
655                         htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
656                         break;
657
658                 case TC_PROVIDES: {
659                         lexrc.next();
660                         string const feature = lexrc.getString();
661                         lexrc.next();
662                         if (lexrc.getInteger())
663                                 provides_.insert(feature);
664                         else
665                                 provides_.erase(feature);
666                         break;
667                 }
668
669                 case TC_REQUIRES: {
670                         lexrc.eatLine();
671                         vector<string> const req
672                                 = getVectorFromString(lexrc.getString());
673                         requires_.insert(req.begin(), req.end());
674                         break;
675                 }
676
677                 case TC_PKGOPTS : {
678                         lexrc.next();
679                         string const pkg = lexrc.getString();
680                         lexrc.next();
681                         string const options = lexrc.getString();
682                         package_options_[pkg] = options;
683                         break;
684                 }
685
686                 case TC_DEFAULTMODULE: {
687                         lexrc.next();
688                         string const module = lexrc.getString();
689                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
690                                 default_modules_.push_back(module);
691                         break;
692                 }
693
694                 case TC_PROVIDESMODULE: {
695                         lexrc.next();
696                         string const module = lexrc.getString();
697                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
698                                 provided_modules_.push_back(module);
699                         break;
700                 }
701
702                 case TC_EXCLUDESMODULE: {
703                         lexrc.next();
704                         string const module = lexrc.getString();
705                         // modules already have their own way to exclude other modules
706                         if (rt == MODULE) {
707                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
708                                 break;
709                         }
710                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
711                                 excluded_modules_.push_back(module);
712                         break;
713                 }
714
715                 case TC_LEFTMARGIN:     // left margin type
716                         if (lexrc.next())
717                                 leftmargin_ = lexrc.getDocString();
718                         break;
719
720                 case TC_RIGHTMARGIN:    // right margin type
721                         if (lexrc.next())
722                                 rightmargin_ = lexrc.getDocString();
723                         break;
724
725                 case TC_INSETLAYOUT: {
726                         if (!lexrc.next()) {
727                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
728                                 error = true;
729                                 break;
730                         }
731                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
732                         if (name.empty()) {
733                                 string s = "Could not read name for InsetLayout: `$$Token' "
734                                         + lexrc.getString() + " is probably not valid UTF-8!";
735                                 lexrc.printError(s);
736                                 InsetLayout il;
737                                 // Since we couldn't read the name, we just scan the rest
738                                 // of the style and discard it.
739                                 il.read(lexrc, *this);
740                                 // Let's try to continue rather than abort.
741                                 // error = true;
742                         } else if (hasInsetLayout(name)) {
743                                 InsetLayout & il = insetlayoutlist_[name];
744                                 error = !il.read(lexrc, *this);
745                         } else {
746                                 InsetLayout il;
747                                 il.setName(name);
748                                 error = !il.read(lexrc, *this);
749                                 if (!error)
750                                         insetlayoutlist_[name] = il;
751                         }
752                         break;
753                 }
754
755                 case TC_FLOAT:
756                         error = !readFloat(lexrc);
757                         break;
758
759                 case TC_CITEENGINE:
760                         error = !readCiteEngine(lexrc);
761                         break;
762
763                 case TC_CITEENGINETYPE:
764                         if (lexrc.next())
765                                 opt_enginetype_ = rtrim(lexrc.getString());
766                         break;
767
768                 case TC_CITEFORMAT:
769                         error = !readCiteFormat(lexrc);
770                         break;
771
772                 case TC_CITEFRAMEWORK:
773                         lexrc.next();
774                         citeframework_ = rtrim(lexrc.getString());
775                         break;
776
777                 case TC_MAXCITENAMES:
778                         lexrc.next();
779                         maxcitenames_ = size_t(lexrc.getInteger());
780                         break;
781
782                 case TC_DEFAULTBIBLIO:
783                         if (lexrc.next()) {
784                                 vector<string> const dbs =
785                                         getVectorFromString(rtrim(lexrc.getString()), "|");
786                                 vector<string>::const_iterator it  = dbs.begin();
787                                 vector<string>::const_iterator end = dbs.end();
788                                 for (; it != end; ++it) {
789                                         if (!contains(*it, ':')) {
790                                                 vector<string> const enginetypes =
791                                                         getVectorFromString(opt_enginetype_, "|");
792                                                 for (string const &s: enginetypes)
793                                                         cite_default_biblio_style_[s] = *it;
794                                         } else {
795                                                 string eng;
796                                                 string const db = split(*it, eng, ':');
797                                                 cite_default_biblio_style_[eng] = db;
798                                         }
799                                 }
800                         }
801                         break;
802
803                 case TC_FULLAUTHORLIST:
804                         if (lexrc.next())
805                                 cite_full_author_list_ &= lexrc.getBool();
806                         break;
807
808                 case TC_NOCOUNTER:
809                         if (lexrc.next()) {
810                                 docstring const cnt = lexrc.getDocString();
811                                 if (!counters_.remove(cnt))
812                                         LYXERR0("Unable to remove counter: " + to_utf8(cnt));
813                         }
814                         break;
815
816                 case TC_IFCOUNTER:
817                         ifcounter = true;
818                         // fall through
819                 case TC_COUNTER:
820                         if (lexrc.next()) {
821                                 docstring const name = lexrc.getDocString();
822                                 if (name.empty()) {
823                                         string s = "Could not read name for counter: `$$Token' "
824                                                         + lexrc.getString() + " is probably not valid UTF-8!";
825                                         lexrc.printError(s.c_str());
826                                         Counter c;
827                                         // Since we couldn't read the name, we just scan the rest
828                                         // and discard it.
829                                         c.read(lexrc);
830                                 } else
831                                         error = !counters_.read(lexrc, name, !ifcounter);
832                         }
833                         else {
834                                 lexrc.printError("No name given for style: `$$Token'.");
835                                 error = true;
836                         }
837                         break;
838
839                 case TC_TITLELATEXTYPE:
840                         readTitleType(lexrc);
841                         break;
842
843                 case TC_TITLELATEXNAME:
844                         if (lexrc.next())
845                                 titlename_ = lexrc.getString();
846                         break;
847
848                 case TC_NOFLOAT:
849                         if (lexrc.next()) {
850                                 string const nofloat = lexrc.getString();
851                                 floatlist_.erase(nofloat);
852                         }
853                         break;
854
855                 case TC_OUTLINERNAME:
856                         error = !readOutlinerName(lexrc);
857                         break;
858                 } // end of switch
859         }
860
861         // at present, we abort if we encounter an error,
862         // so there is no point continuing.
863         if (error)
864                 return ERROR;
865
866         if (rt != BASECLASS)
867                 return OK;
868
869         if (defaultlayout_.empty()) {
870                 LYXERR0("Error: Textclass '" << name_
871                                                 << "' is missing a defaultstyle.");
872                 return ERROR;
873         }
874
875         // Try to erase "stdinsets" from the provides_ set.
876         // The
877         //   Provides stdinsets 1
878         // declaration simply tells us that the standard insets have been
879         // defined. (It's found in stdinsets.inc but could also be used in
880         // user-defined files.) There isn't really any such package. So we
881         // might as well go ahead and erase it.
882         // If we do not succeed, then it was not there, which means that
883         // the textclass did not provide the definitions of the standard
884         // insets. So we need to try to load them.
885         int erased = provides_.erase("stdinsets");
886         if (!erased) {
887                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
888
889                 if (tmp.empty()) {
890                         frontend::Alert::warning(_("Missing File"),
891                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
892                         error = true;
893                 } else if (!read(tmp, MERGE)) {
894                         frontend::Alert::warning(_("Corrupt File"),
895                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
896                         error = true;
897                 }
898         }
899
900         min_toclevel_ = Layout::NOT_IN_TOC;
901         max_toclevel_ = Layout::NOT_IN_TOC;
902         const_iterator lit = begin();
903         const_iterator len = end();
904         for (; lit != len; ++lit) {
905                 int const toclevel = lit->toclevel;
906                 if (toclevel != Layout::NOT_IN_TOC) {
907                         if (min_toclevel_ == Layout::NOT_IN_TOC)
908                                 min_toclevel_ = toclevel;
909                         else
910                                 min_toclevel_ = min(min_toclevel_, toclevel);
911                         max_toclevel_ = max(max_toclevel_, toclevel);
912                 }
913         }
914         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
915                 << ", maximum is " << max_toclevel_);
916
917         return (error ? ERROR : OK);
918 }
919
920
921 void TextClass::readTitleType(Lexer & lexrc)
922 {
923         LexerKeyword titleTypeTags[] = {
924                 { "commandafter", TITLE_COMMAND_AFTER },
925                 { "environment",  TITLE_ENVIRONMENT }
926         };
927
928         PushPopHelper pph(lexrc, titleTypeTags);
929
930         int le = lexrc.lex();
931         switch (le) {
932         case Lexer::LEX_UNDEF:
933                 lexrc.printError("Unknown output type `$$Token'");
934                 break;
935         case TITLE_COMMAND_AFTER:
936         case TITLE_ENVIRONMENT:
937                 titletype_ = static_cast<TitleLatexType>(le);
938                 break;
939         default:
940                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
941                 break;
942         }
943 }
944
945
946 void TextClass::readOutputType(Lexer & lexrc)
947 {
948         LexerKeyword outputTypeTags[] = {
949                 { "docbook",  DOCBOOK },
950                 { "latex",    LATEX },
951                 { "literate", LITERATE }
952         };
953
954         PushPopHelper pph(lexrc, outputTypeTags);
955
956         int le = lexrc.lex();
957         switch (le) {
958         case Lexer::LEX_UNDEF:
959                 lexrc.printError("Unknown output type `$$Token'");
960                 return;
961         case LATEX:
962         case DOCBOOK:
963         case LITERATE:
964                 outputType_ = static_cast<OutputType>(le);
965                 break;
966         default:
967                 LYXERR0("Unhandled value " << le);
968                 break;
969         }
970 }
971
972
973 void TextClass::readClassOptions(Lexer & lexrc)
974 {
975         enum {
976                 CO_FONTSIZE = 1,
977                 CO_PAGESTYLE,
978                 CO_OTHER,
979                 CO_HEADER,
980                 CO_END
981         };
982
983         LexerKeyword classOptionsTags[] = {
984                 {"end",       CO_END },
985                 {"fontsize",  CO_FONTSIZE },
986                 {"header",    CO_HEADER },
987                 {"other",     CO_OTHER },
988                 {"pagestyle", CO_PAGESTYLE }
989         };
990
991         lexrc.pushTable(classOptionsTags);
992         bool getout = false;
993         while (!getout && lexrc.isOK()) {
994                 int le = lexrc.lex();
995                 switch (le) {
996                 case Lexer::LEX_UNDEF:
997                         lexrc.printError("Unknown ClassOption tag `$$Token'");
998                         continue;
999                 default:
1000                         break;
1001                 }
1002                 switch (le) {
1003                 case CO_FONTSIZE:
1004                         lexrc.next();
1005                         opt_fontsize_ = rtrim(lexrc.getString());
1006                         break;
1007                 case CO_PAGESTYLE:
1008                         lexrc.next();
1009                         opt_pagestyle_ = rtrim(lexrc.getString());
1010                         break;
1011                 case CO_OTHER:
1012                         lexrc.next();
1013                         if (options_.empty())
1014                                 options_ = lexrc.getString();
1015                         else
1016                                 options_ += ',' + lexrc.getString();
1017                         break;
1018                 case CO_HEADER:
1019                         lexrc.next();
1020                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
1021                         break;
1022                 case CO_END:
1023                         getout = true;
1024                         break;
1025                 }
1026         }
1027         lexrc.popTable();
1028 }
1029
1030
1031 bool TextClass::readCiteEngine(Lexer & lexrc)
1032 {
1033         int const type = readCiteEngineType(lexrc);
1034         if (type & ENGINE_TYPE_AUTHORYEAR)
1035                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1036         if (type & ENGINE_TYPE_NUMERICAL)
1037                 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1038         if (type & ENGINE_TYPE_DEFAULT)
1039                 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1040         string def;
1041         bool getout = false;
1042         while (!getout && lexrc.isOK()) {
1043                 lexrc.eatLine();
1044                 def = lexrc.getString();
1045                 def = subst(def, " ", "");
1046                 def = subst(def, "\t", "");
1047                 if (compare_ascii_no_case(def, "end") == 0) {
1048                         getout = true;
1049                         continue;
1050                 }
1051                 CitationStyle cs;
1052                 char ichar = def[0];
1053                 if (ichar == '#')
1054                         continue;
1055                 if (isUpperCase(ichar)) {
1056                         cs.forceUpperCase = true;
1057                         def[0] = lowercase(ichar);
1058                 }
1059
1060                 /** For portability reasons (between different
1061                  *  cite engines such as natbib and biblatex),
1062                  *  we distinguish between:
1063                  *  1. The LyX name as output in the LyX file
1064                  *  2. Possible aliases that might fall back to
1065                  *     the given LyX name in the current engine
1066                  *  3. The actual LaTeX command that is output
1067                  *  (2) and (3) are optional.
1068                  *  Also, the GUI string for the starred version can
1069                  *  be changed
1070                  *  The syntax is:
1071                  *  LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1072                  */
1073                 enum ScanMode {
1074                         LyXName,
1075                         Alias,
1076                         LaTeXCmd,
1077                         StarDesc
1078                 };
1079
1080                 ScanMode mode = LyXName;
1081                 ScanMode oldmode = LyXName;
1082                 string lyx_cmd;
1083                 string alias;
1084                 string latex_cmd;
1085                 string stardesc;
1086                 size_t const n = def.size();
1087                 for (size_t i = 0; i != n; ++i) {
1088                         ichar = def[i];
1089                         if (ichar == '|')
1090                                 mode = Alias;
1091                         else if (ichar == '=')
1092                                 mode = LaTeXCmd;
1093                         else if (ichar == '<') {
1094                                 oldmode = mode;
1095                                 mode = StarDesc;
1096                         } else if (ichar == '>')
1097                                 mode = oldmode;
1098                         else if (mode == LaTeXCmd)
1099                                 latex_cmd += ichar;
1100                         else if (mode == StarDesc)
1101                                 stardesc += ichar;
1102                         else if (ichar == '$')
1103                                 cs.hasQualifiedList = true;
1104                         else if (ichar == '*')
1105                                 cs.hasStarredVersion = true;
1106                         else if (ichar == '[' && cs.textAfter)
1107                                 cs.textBefore = true;
1108                         else if (ichar == '[')
1109                                 cs.textAfter = true;
1110                         else if (ichar != ']') {
1111                                 if (mode == Alias)
1112                                         alias += ichar;
1113                                 else
1114                                         lyx_cmd += ichar;
1115                         }
1116                 }
1117                 cs.name = lyx_cmd;
1118                 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1119                 if (!alias.empty()) {
1120                         vector<string> const aliases = getVectorFromString(alias);
1121                         for (string const &s: aliases)
1122                                 cite_command_aliases_[s] = lyx_cmd;
1123                 }
1124                 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1125                 int size = stardesc.size();
1126                 if (size > 0)
1127                         cs.stardesc = stardescs[0];
1128                 if (size > 1)
1129                         cs.startooltip = stardescs[1];
1130                 if (type & ENGINE_TYPE_AUTHORYEAR)
1131                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1132                 if (type & ENGINE_TYPE_NUMERICAL)
1133                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1134                 if (type & ENGINE_TYPE_DEFAULT)
1135                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1136         }
1137         return getout;
1138 }
1139
1140
1141 int TextClass::readCiteEngineType(Lexer & lexrc) const
1142 {
1143         LATTEST(ENGINE_TYPE_DEFAULT ==
1144                 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1145         if (!lexrc.next()) {
1146                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1147                 return ENGINE_TYPE_DEFAULT;
1148         }
1149         string const type = rtrim(lexrc.getString());
1150         if (compare_ascii_no_case(type, "authoryear") == 0)
1151                 return ENGINE_TYPE_AUTHORYEAR;
1152         else if (compare_ascii_no_case(type, "numerical") == 0)
1153                 return ENGINE_TYPE_NUMERICAL;
1154         else if (compare_ascii_no_case(type, "default") != 0) {
1155                 string const s = "Unknown cite engine type `" + type
1156                         + "' given for token: `$$Token',";
1157                 lexrc.printError(s);
1158         }
1159         return ENGINE_TYPE_DEFAULT;
1160 }
1161
1162
1163 bool TextClass::readCiteFormat(Lexer & lexrc)
1164 {
1165         int const type = readCiteEngineType(lexrc);
1166         string etype;
1167         string definition;
1168         while (lexrc.isOK()) {
1169                 lexrc.next();
1170                 etype = lexrc.getString();
1171                 if (compare_ascii_no_case(etype, "end") == 0)
1172                         break;
1173                 if (!lexrc.isOK())
1174                         return false;
1175                 lexrc.eatLine();
1176                 definition = lexrc.getString();
1177                 char initchar = etype[0];
1178                 if (initchar == '#')
1179                         continue;
1180                 if (initchar == '!' || initchar == '_') {
1181                         if (type & ENGINE_TYPE_AUTHORYEAR)
1182                                 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1183                         if (type & ENGINE_TYPE_NUMERICAL)
1184                                 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1185                         if (type & ENGINE_TYPE_DEFAULT)
1186                                 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1187                 } else {
1188                         if (type & ENGINE_TYPE_AUTHORYEAR)
1189                                 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1190                         if (type & ENGINE_TYPE_NUMERICAL)
1191                                 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1192                         if (type & ENGINE_TYPE_DEFAULT)
1193                                 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1194                 }
1195         }
1196         return true;
1197 }
1198
1199
1200 bool TextClass::readFloat(Lexer & lexrc)
1201 {
1202         enum {
1203                 FT_TYPE = 1,
1204                 FT_NAME,
1205                 FT_PLACEMENT,
1206                 FT_EXT,
1207                 FT_WITHIN,
1208                 FT_STYLE,
1209                 FT_LISTNAME,
1210                 FT_USESFLOAT,
1211                 FT_PREDEFINED,
1212                 FT_HTMLSTYLE,
1213                 FT_HTMLATTR,
1214                 FT_HTMLTAG,
1215                 FT_LISTCOMMAND,
1216                 FT_REFPREFIX,
1217                 FT_ALLOWED_PLACEMENT,
1218                 FT_ALLOWS_SIDEWAYS,
1219                 FT_ALLOWS_WIDE,
1220                 FT_END
1221         };
1222
1223         LexerKeyword floatTags[] = {
1224                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1225                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1226                 { "allowswide", FT_ALLOWS_WIDE },
1227                 { "end", FT_END },
1228                 { "extension", FT_EXT },
1229                 { "guiname", FT_NAME },
1230                 { "htmlattr", FT_HTMLATTR },
1231                 { "htmlstyle", FT_HTMLSTYLE },
1232                 { "htmltag", FT_HTMLTAG },
1233                 { "ispredefined", FT_PREDEFINED },
1234                 { "listcommand", FT_LISTCOMMAND },
1235                 { "listname", FT_LISTNAME },
1236                 { "numberwithin", FT_WITHIN },
1237                 { "placement", FT_PLACEMENT },
1238                 { "refprefix", FT_REFPREFIX },
1239                 { "style", FT_STYLE },
1240                 { "type", FT_TYPE },
1241                 { "usesfloatpkg", FT_USESFLOAT }
1242         };
1243
1244         lexrc.pushTable(floatTags);
1245
1246         string ext;
1247         string htmlattr;
1248         docstring htmlstyle;
1249         string htmltag;
1250         string listname;
1251         string listcommand;
1252         string name;
1253         string placement;
1254         string allowed_placement = "!htbpH";
1255         string refprefix;
1256         string style;
1257         string type;
1258         string within;
1259         bool usesfloat = true;
1260         bool ispredefined = false;
1261         bool allowswide = true;
1262         bool allowssideways = true;
1263
1264         bool getout = false;
1265         while (!getout && lexrc.isOK()) {
1266                 int le = lexrc.lex();
1267                 switch (le) {
1268                 case Lexer::LEX_UNDEF:
1269                         lexrc.printError("Unknown float tag `$$Token'");
1270                         continue;
1271                 default:
1272                         break;
1273                 }
1274                 switch (le) {
1275                 case FT_TYPE:
1276                         lexrc.next();
1277                         type = lexrc.getString();
1278                         if (floatlist_.typeExist(type)) {
1279                                 Floating const & fl = floatlist_.getType(type);
1280                                 placement = fl.placement();
1281                                 ext = fl.ext();
1282                                 within = fl.within();
1283                                 style = fl.style();
1284                                 name = fl.name();
1285                                 listname = fl.listName();
1286                                 usesfloat = fl.usesFloatPkg();
1287                                 ispredefined = fl.isPredefined();
1288                                 listcommand = fl.listCommand();
1289                                 refprefix = fl.refPrefix();
1290                         }
1291                         break;
1292                 case FT_NAME:
1293                         lexrc.next();
1294                         name = lexrc.getString();
1295                         break;
1296                 case FT_PLACEMENT:
1297                         lexrc.next();
1298                         placement = lexrc.getString();
1299                         break;
1300                 case FT_ALLOWED_PLACEMENT:
1301                         lexrc.next();
1302                         allowed_placement = lexrc.getString();
1303                         break;
1304                 case FT_EXT:
1305                         lexrc.next();
1306                         ext = lexrc.getString();
1307                         break;
1308                 case FT_WITHIN:
1309                         lexrc.next();
1310                         within = lexrc.getString();
1311                         if (within == "none")
1312                                 within.erase();
1313                         break;
1314                 case FT_STYLE:
1315                         lexrc.next();
1316                         style = lexrc.getString();
1317                         break;
1318                 case FT_LISTCOMMAND:
1319                         lexrc.next();
1320                         listcommand = lexrc.getString();
1321                         break;
1322                 case FT_REFPREFIX:
1323                         lexrc.next();
1324                         refprefix = lexrc.getString();
1325                         break;
1326                 case FT_LISTNAME:
1327                         lexrc.next();
1328                         listname = lexrc.getString();
1329                         break;
1330                 case FT_USESFLOAT:
1331                         lexrc.next();
1332                         usesfloat = lexrc.getBool();
1333                         break;
1334                 case FT_PREDEFINED:
1335                         lexrc.next();
1336                         ispredefined = lexrc.getBool();
1337                         break;
1338                 case FT_ALLOWS_SIDEWAYS:
1339                         lexrc.next();
1340                         allowssideways = lexrc.getBool();
1341                         break;
1342                 case FT_ALLOWS_WIDE:
1343                         lexrc.next();
1344                         allowswide = lexrc.getBool();
1345                         break;
1346                 case FT_HTMLATTR:
1347                         lexrc.next();
1348                         htmlattr = lexrc.getString();
1349                         break;
1350                 case FT_HTMLSTYLE:
1351                         lexrc.next();
1352                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1353                         break;
1354                 case FT_HTMLTAG:
1355                         lexrc.next();
1356                         htmltag = lexrc.getString();
1357                         break;
1358                 case FT_END:
1359                         getout = true;
1360                         break;
1361                 }
1362         }
1363
1364         lexrc.popTable();
1365
1366         // Here we have a full float if getout == true
1367         if (getout) {
1368                 if (!usesfloat && listcommand.empty()) {
1369                         // if this float uses the same auxfile as an existing one,
1370                         // there is no need for it to provide a list command.
1371                         FloatList::const_iterator it = floatlist_.begin();
1372                         FloatList::const_iterator en = floatlist_.end();
1373                         bool found_ext = false;
1374                         for (; it != en; ++it) {
1375                                 if (it->second.ext() == ext) {
1376                                         found_ext = true;
1377                                         break;
1378                                 }
1379                         }
1380                         if (!found_ext)
1381                                 LYXERR0("The layout does not provide a list command " <<
1382                                   "for the float `" << type << "'. LyX will " <<
1383                                   "not be able to produce a float list.");
1384                 }
1385                 Floating fl(type, placement, ext, within, style, name,
1386                             listname, listcommand, refprefix, allowed_placement,
1387                             htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1388                             allowswide, allowssideways);
1389                 floatlist_.newFloat(fl);
1390                 // each float has its own counter
1391                 counters_.newCounter(from_ascii(type), from_ascii(within),
1392                                       docstring(), docstring());
1393                 // also define sub-float counters
1394                 docstring const subtype = "sub-" + from_ascii(type);
1395                 counters_.newCounter(subtype, from_ascii(type),
1396                                       "\\alph{" + subtype + "}", docstring());
1397         }
1398         return getout;
1399 }
1400
1401
1402 bool TextClass::readOutlinerName(Lexer & lexrc)
1403 {
1404         std::string type;
1405         docstring name;
1406         if (lexrc.next())
1407                 type = lexrc.getString();
1408         else {
1409                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1410                 return false;
1411         }
1412         if (lexrc.next())
1413                 name = lexrc.getDocString();
1414         else {
1415                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1416                 return false;
1417         }
1418         outliner_names_[type] = name;
1419     return true;
1420 }
1421
1422
1423 string const & TextClass::prerequisites(string const & sep) const
1424 {
1425         if (contains(prerequisites_, ',')) {
1426                 vector<string> const pres = getVectorFromString(prerequisites_);
1427                 prerequisites_ = getStringFromVector(pres, sep);
1428         }
1429         return prerequisites_;
1430 }
1431
1432
1433 bool TextClass::hasLayout(docstring const & n) const
1434 {
1435         docstring const name = n.empty() ? defaultLayoutName() : n;
1436
1437         return find_if(layoutlist_.begin(), layoutlist_.end(),
1438                        LayoutNamesEqual(name))
1439                 != layoutlist_.end();
1440 }
1441
1442
1443 bool TextClass::hasInsetLayout(docstring const & n) const
1444 {
1445         if (n.empty())
1446                 return false;
1447         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1448         return it != insetlayoutlist_.end();
1449 }
1450
1451
1452 Layout const & TextClass::operator[](docstring const & name) const
1453 {
1454         LATTEST(!name.empty());
1455
1456         const_iterator it =
1457                 find_if(begin(), end(), LayoutNamesEqual(name));
1458
1459         if (it == end()) {
1460                 LYXERR0("We failed to find the layout '" << name
1461                        << "' in the layout list. You MUST investigate!");
1462                 for (const_iterator cit = begin(); cit != end(); ++cit)
1463                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1464
1465                 // We require the name to exist
1466                 static const Layout dummy;
1467                 LASSERT(false, return dummy);
1468         }
1469
1470         return *it;
1471 }
1472
1473
1474 Layout & TextClass::operator[](docstring const & name)
1475 {
1476         LATTEST(!name.empty());
1477         // Safe to continue, given what we do below.
1478
1479         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1480
1481         if (it == end()) {
1482                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1483                        << "' in the layout list. You MUST investigate!");
1484                 for (const_iterator cit = begin(); cit != end(); ++cit)
1485                         LYXERR0(" " << to_utf8(cit->name()));
1486
1487                 // we require the name to exist
1488                 LATTEST(false);
1489                 // we are here only in release mode
1490                 layoutlist_.push_back(createBasicLayout(name, true));
1491                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1492         }
1493
1494         return *it;
1495 }
1496
1497
1498 bool TextClass::deleteLayout(docstring const & name)
1499 {
1500         if (name == defaultLayoutName() || name == plainLayoutName())
1501                 return false;
1502
1503         LayoutList::iterator it =
1504                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1505                           LayoutNamesEqual(name));
1506
1507         LayoutList::iterator end = layoutlist_.end();
1508         bool const ret = (it != end);
1509         layoutlist_.erase(it, end);
1510         return ret;
1511 }
1512
1513
1514 bool TextClass::deleteInsetLayout(docstring const & name)
1515 {
1516         return insetlayoutlist_.erase(name);
1517 }
1518
1519
1520 // Load textclass info if not loaded yet
1521 bool TextClass::load(string const & path) const
1522 {
1523         if (loaded_)
1524                 return true;
1525
1526         // Read style-file, provided path is searched before the system ones
1527         // If path is a file, it is loaded directly.
1528         FileName layout_file(path);
1529         if (!path.empty() && !layout_file.isReadableFile())
1530                 layout_file = FileName(addName(path, name_ + ".layout"));
1531         if (layout_file.empty() || !layout_file.exists())
1532                 layout_file = libFileSearch("layouts", name_, "layout");
1533         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1534
1535         if (!loaded_) {
1536                 lyxerr << "Error reading `"
1537                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1538                        << "'\n(Check `" << name_
1539                        << "')\nCheck your installation and "
1540                           "try Options/Reconfigure..."
1541                        << endl;
1542         }
1543
1544         return loaded_;
1545 }
1546
1547
1548 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1549 {
1550         if (hasLayout(n))
1551                 return false;
1552
1553         layoutlist_.push_back(createBasicLayout(n, true));
1554         return true;
1555 }
1556
1557
1558 string DocumentClass::forcedLayouts() const
1559 {
1560         ostringstream os;
1561         bool first = true;
1562         const_iterator const e = end();
1563         for (const_iterator i = begin(); i != e; ++i) {
1564                 if (i->forcelocal > 0) {
1565                         if (first) {
1566                                 os << "Format " << LAYOUT_FORMAT << '\n';
1567                                 first = false;
1568                         }
1569                         i->write(os);
1570                 }
1571         }
1572         return os.str();
1573 }
1574
1575
1576 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1577 {
1578         // FIXME The fix for the InsetLayout part of 4812 would be here:
1579         // Add the InsetLayout to the document class if it is not found.
1580         docstring n = name;
1581         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1582         while (!n.empty()) {
1583                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1584                 if (cit != cen && cit->first == n) {
1585                         if (cit->second.obsoleted_by().empty())
1586                                 return cit->second;
1587                         n = cit->second.obsoleted_by();
1588                         return insetLayout(n);
1589                 }
1590                 // If we have a generic prefix (e.g., "Note:"),
1591                 // try if this one alone is found.
1592                 size_t i = n.find(':');
1593                 if (i == string::npos)
1594                         break;
1595                 n = n.substr(0, i);
1596         }
1597         // Layout "name" not found.
1598         return plainInsetLayout();
1599 }
1600
1601
1602 InsetLayout const & DocumentClass::plainInsetLayout() {
1603         static const InsetLayout plain_insetlayout_;
1604         return plain_insetlayout_;
1605 }
1606
1607
1608 docstring const & TextClass::defaultLayoutName() const
1609 {
1610         return defaultlayout_;
1611 }
1612
1613
1614 Layout const & TextClass::defaultLayout() const
1615 {
1616         return operator[](defaultLayoutName());
1617 }
1618
1619
1620 bool TextClass::isDefaultLayout(Layout const & layout) const
1621 {
1622         return layout.name() == defaultLayoutName();
1623 }
1624
1625
1626 bool TextClass::isPlainLayout(Layout const & layout) const
1627 {
1628         return layout.name() == plainLayoutName();
1629 }
1630
1631
1632 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1633 {
1634         static Layout * defaultLayout = NULL;
1635
1636         if (defaultLayout) {
1637                 defaultLayout->setUnknown(unknown);
1638                 defaultLayout->setName(name);
1639                 return *defaultLayout;
1640         }
1641
1642         static char const * s = "Margin Static\n"
1643                         "LatexType Paragraph\n"
1644                         "LatexName dummy\n"
1645                         "Align Block\n"
1646                         "AlignPossible Left, Right, Center\n"
1647                         "LabelType No_Label\n"
1648                         "End";
1649         istringstream ss(s);
1650         Lexer lex(textClassTags);
1651         lex.setStream(ss);
1652         defaultLayout = new Layout;
1653         defaultLayout->setUnknown(unknown);
1654         defaultLayout->setName(name);
1655         if (!readStyle(lex, *defaultLayout)) {
1656                 // The only way this happens is because the hardcoded layout above
1657                 // is wrong.
1658                 LATTEST(false);
1659         };
1660         return *defaultLayout;
1661 }
1662
1663
1664 DocumentClassPtr getDocumentClass(
1665                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1666                 LayoutModuleList const & celist,
1667                 bool const clone)
1668 {
1669         DocumentClassPtr doc_class =
1670             DocumentClassPtr(new DocumentClass(baseClass));
1671         LayoutModuleList::const_iterator it = modlist.begin();
1672         LayoutModuleList::const_iterator en = modlist.end();
1673         for (; it != en; ++it) {
1674                 string const modName = *it;
1675                 LyXModule * lm = theModuleList[modName];
1676                 if (!lm) {
1677                         docstring const msg =
1678                                                 bformat(_("The module %1$s has been requested by\n"
1679                                                 "this document but has not been found in the list of\n"
1680                                                 "available modules. If you recently installed it, you\n"
1681                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1682                         if (!clone)
1683                                 frontend::Alert::warning(_("Module not available"), msg);
1684                         continue;
1685                 }
1686                 if (!lm->isAvailable() && !clone) {
1687                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1688                         docstring const msg =
1689                                 bformat(_("The module %1$s requires a package that is not\n"
1690                                         "available in your LaTeX installation, or a converter that\n"
1691                                         "you have not installed. LaTeX output may not be possible.\n"
1692                                         "Missing prerequisites:\n"
1693                                                 "\t%2$s\n"
1694                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1695                                 from_utf8(modName), prereqs);
1696                         frontend::Alert::warning(_("Package not available"), msg, true);
1697                 }
1698                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1699                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1700                         docstring const msg =
1701                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1702                         frontend::Alert::warning(_("Read Error"), msg);
1703                 }
1704         }
1705
1706         LayoutModuleList::const_iterator cit = celist.begin();
1707         LayoutModuleList::const_iterator cen = celist.end();
1708         for (; cit != cen; ++cit) {
1709                 string const ceName = *cit;
1710                 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1711                 if (!ce) {
1712                         docstring const msg =
1713                                                 bformat(_("The cite engine %1$s has been requested by\n"
1714                                                 "this document but has not been found in the list of\n"
1715                                                 "available engines. If you recently installed it, you\n"
1716                                                 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1717                         if (!clone)
1718                                 frontend::Alert::warning(_("Cite Engine not available"), msg);
1719                         continue;
1720                 }
1721                 if (!ce->isAvailable() && !clone) {
1722                         docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1723                         docstring const msg =
1724                                 bformat(_("The cite engine %1$s requires a package that is not\n"
1725                                         "available in your LaTeX installation, or a converter that\n"
1726                                         "you have not installed. LaTeX output may not be possible.\n"
1727                                         "Missing prerequisites:\n"
1728                                                 "\t%2$s\n"
1729                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1730                                 from_utf8(ceName), prereqs);
1731                         frontend::Alert::warning(_("Package not available"), msg, true);
1732                 }
1733                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1734                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1735                         docstring const msg =
1736                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1737                         frontend::Alert::warning(_("Read Error"), msg);
1738                 }
1739         }
1740
1741         return doc_class;
1742 }
1743
1744
1745 /////////////////////////////////////////////////////////////////////////
1746 //
1747 // DocumentClass
1748 //
1749 /////////////////////////////////////////////////////////////////////////
1750
1751 DocumentClass::DocumentClass(LayoutFile const & tc)
1752         : TextClass(tc)
1753 {}
1754
1755
1756 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1757 {
1758         LayoutList::const_iterator it  = layoutlist_.begin();
1759         LayoutList::const_iterator end = layoutlist_.end();
1760         for (; it != end; ++it)
1761                 if (it->latexname() == lay)
1762                         return true;
1763         return false;
1764 }
1765
1766
1767 bool DocumentClass::provides(string const & p) const
1768 {
1769         return provides_.find(p) != provides_.end();
1770 }
1771
1772
1773 bool DocumentClass::hasTocLevels() const
1774 {
1775         return min_toclevel_ != Layout::NOT_IN_TOC;
1776 }
1777
1778
1779 Layout const & DocumentClass::getTOCLayout() const
1780 {
1781         // we're going to look for the layout with the minimum toclevel
1782         TextClass::LayoutList::const_iterator lit = begin();
1783         TextClass::LayoutList::const_iterator const len = end();
1784         int minlevel = 1000;
1785         Layout const * lay = NULL;
1786         for (; lit != len; ++lit) {
1787                 int const level = lit->toclevel;
1788                 // we don't want Part or unnumbered sections
1789                 if (level == Layout::NOT_IN_TOC || level < 0
1790                     || level >= minlevel || lit->counter.empty())
1791                         continue;
1792                 lay = &*lit;
1793                 minlevel = level;
1794         }
1795         if (lay)
1796                 return *lay;
1797         // hmm. that is very odd, so we'll do our best.
1798         return operator[](defaultLayoutName());
1799 }
1800
1801
1802 Layout const & DocumentClass::htmlTOCLayout() const
1803 {
1804         if (html_toc_section_.empty())
1805                 html_toc_section_ = getTOCLayout().name();
1806         return operator[](html_toc_section_);
1807 }
1808
1809
1810 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1811         string const & entry, bool const punct, string const & fallback) const
1812 {
1813         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%]]}";
1814         if (punct)
1815                 default_format += ".";
1816
1817         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1818         if (itype == cite_formats_.end())
1819                 return default_format;
1820         map<string, string>::const_iterator it = itype->second.find(entry);
1821         if (it == itype->second.end() && !fallback.empty())
1822                 it = itype->second.find(fallback);
1823         if (it == itype->second.end())
1824                 return default_format;
1825         if (punct)
1826                 return it->second + ".";
1827         return it->second;
1828 }
1829
1830
1831 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1832         string const & macro) const
1833 {
1834         static string empty;
1835         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1836         if (itype == cite_macros_.end())
1837                 return empty;
1838         map<string, string>::const_iterator it = itype->second.find(macro);
1839         if (it == itype->second.end())
1840                 return empty;
1841         return it->second;
1842 }
1843
1844
1845 vector<string> const DocumentClass::citeCommands(
1846         CiteEngineType const & type) const
1847 {
1848         vector<CitationStyle> const styles = citeStyles(type);
1849         vector<CitationStyle>::const_iterator it = styles.begin();
1850         vector<CitationStyle>::const_iterator end = styles.end();
1851         vector<string> cmds;
1852         for (; it != end; ++it) {
1853                 CitationStyle const cite = *it;
1854                 cmds.push_back(cite.name);
1855         }
1856         return cmds;
1857 }
1858
1859
1860 vector<CitationStyle> const & DocumentClass::citeStyles(
1861         CiteEngineType const & type) const
1862 {
1863         static vector<CitationStyle> empty;
1864         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1865         if (it == cite_styles_.end())
1866                 return empty;
1867         return it->second;
1868 }
1869
1870
1871 /////////////////////////////////////////////////////////////////////////
1872 //
1873 // PageSides
1874 //
1875 /////////////////////////////////////////////////////////////////////////
1876
1877 ostream & operator<<(ostream & os, PageSides p)
1878 {
1879         switch (p) {
1880         case OneSide:
1881                 os << '1';
1882                 break;
1883         case TwoSides:
1884                 os << '2';
1885                 break;
1886         }
1887         return os;
1888 }
1889
1890
1891 } // namespace lyx