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