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