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