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