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