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