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