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