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