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