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