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