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