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